From ffd7c6110b379115cef52fc40e03fdcf50eecb5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20A=C3=9Fmus?= Date: Sat, 30 Aug 2014 23:24:40 +0200 Subject: [PATCH] JSON parser: Improve by using exceptions to get exact error Also strip redundant "_Parser_" prefix from private methods. --- headers/private/shared/Json.h | 9 ++- src/kits/shared/Json.cpp | 137 ++++++++++++++++++++++++---------- 2 files changed, 102 insertions(+), 44 deletions(-) diff --git a/headers/private/shared/Json.h b/headers/private/shared/Json.h index 440bf36361..6df2648e63 100644 --- a/headers/private/shared/Json.h +++ b/headers/private/shared/Json.h @@ -22,10 +22,11 @@ public: static status_t Parse(BMessage& message, BString& JSON); private: - static BString _Parser_ParseString(BString& JSON, int32& pos); - static double _Parser_ParseNumber(BString& JSON, int32& pos); - static bool _Parser_ParseConstant(BString& JSON, int32& pos, - const char* constant); + static void _Parse(BMessage& message, BString& JSON); + static BString _ParseString(BString& JSON, int32& pos); + static double _ParseNumber(BString& JSON, int32& pos); + static bool _ParseConstant(BString& JSON, int32& pos, + const char* constant); }; } // namespace BPrivate diff --git a/src/kits/shared/Json.cpp b/src/kits/shared/Json.cpp index 9686266847..3e4a1461eb 100644 --- a/src/kits/shared/Json.cpp +++ b/src/kits/shared/Json.cpp @@ -1,5 +1,6 @@ /* * Copyright 2014, Augustin Cavalier (waddlesplash) + * Copyright 2014, Stephan Aßmus * Distributed under the terms of the MIT License. */ @@ -17,6 +18,46 @@ namespace BPrivate { + +class ParseException { +public: + ParseException(int32 position, BString error) + : + fPosition(position), + fError(error), + fReturnCode(B_BAD_DATA) + { + } + + ParseException(int32 position, status_t returnCode) + : + fPosition(position), + fError(""), + fReturnCode(returnCode) + { + } + + void PrintToStream() const { + const char* error; + if (fError.Length() > 0) + error = fError.String(); + else + error = strerror(fReturnCode); + printf("Parse error at %ld: %s\n", fPosition, error); + } + + status_t ReturnCode() const + { + return fReturnCode; + } + +private: + int32 fPosition; + BString fError; + status_t fReturnCode; +}; + + status_t BJson::Parse(BMessage& message, const char* JSON) { @@ -27,6 +68,23 @@ BJson::Parse(BMessage& message, const char* JSON) status_t BJson::Parse(BMessage& message, BString& JSON) +{ + try { + _Parse(message, JSON); + return B_OK; + } catch (ParseException e) { + e.PrintToStream(); + return e.ReturnCode(); + } + return B_ERROR; +} + + +// #pragma mark - Private methods + + +void +BJson::_Parse(BMessage& message, BString& JSON) { BMessageBuilder builder(message); int32 pos = 0; @@ -59,13 +117,13 @@ BJson::Parse(BMessage& message, BString& JSON) break; case '}': - if (hierarchy.EndsWith("{") && (hierarchy.Length() != 1)) { + if (hierarchy.EndsWith("{") && hierarchy.Length() != 1) { hierarchy.Truncate(hierarchy.Length() - 1); builder.PopObject(); } else if (hierarchy.Length() == 1) - return B_OK; // End of the JSON data + return; // End of the JSON data else - return B_BAD_DATA; // Unmatched closebrace + throw ParseException(pos, "Unmatched closebrace }"); break; @@ -86,78 +144,80 @@ BJson::Parse(BMessage& message, BString& JSON) if (hierarchy.EndsWith("[")) { hierarchy.Truncate(hierarchy.Length() - 1); builder.PopObject(); - } else - return B_BAD_DATA; // Unmatched closebracket + } else { + BString error("Unmatched closebrace ] hierarchy: "); + error << hierarchy; + throw ParseException(pos, error); + } break; case 't': { - if (builder.What() != JSON_TYPE_ARRAY && key.Length() == 0) - return B_BAD_DATA; - // 'true' cannot be a key, it can only be a value + if (builder.What() != JSON_TYPE_ARRAY && key.Length() == 0) { + throw ParseException(pos, + "'true' cannot be a key, it can only be a value"); + } - if (_Parser_ParseConstant(JSON, pos, "true")) { + if (_ParseConstant(JSON, pos, "true")) { if (builder.What() == JSON_TYPE_ARRAY) key.SetToFormat("%" B_PRIu32, builder.CountNames()); builder.AddBool(key.String(), true); key = ""; } else - return B_BAD_DATA; - // "t" out in the middle of nowhere!? + throw ParseException(pos, "Unexpected 't'"); break; } case 'f': { - if (builder.What() != JSON_TYPE_ARRAY && key.Length() == 0) - return B_BAD_DATA; - // 'false' cannot be a key, it can only be a value + if (builder.What() != JSON_TYPE_ARRAY && key.Length() == 0) { + throw ParseException(pos, + "'false' cannot be a key, it can only be a value"); + } - if (_Parser_ParseConstant(JSON, pos, "false")) { + if (_ParseConstant(JSON, pos, "false")) { if (builder.What() == JSON_TYPE_ARRAY) key.SetToFormat("%" B_PRIu32, builder.CountNames()); builder.AddBool(key.String(), false); key = ""; } else - return B_BAD_DATA; - // "f" out in the middle of nowhere!? + throw ParseException(pos, "Unexpected 'f'"); break; } case 'n': { - if (builder.What() != JSON_TYPE_ARRAY && key.Length() == 0) - return B_BAD_DATA; - // 'null' cannot be a key, it can only be a value + if (builder.What() != JSON_TYPE_ARRAY && key.Length() == 0) { + throw ParseException(pos, + "'null' cannot be a key, it can only be a value"); + } - if (_Parser_ParseConstant(JSON, pos, "null")) { + if (_ParseConstant(JSON, pos, "null")) { if (builder.What() == JSON_TYPE_ARRAY) key.SetToFormat("%" B_PRIu32, builder.CountNames()); builder.AddPointer(key.String(), (void*)NULL); key = ""; } else - return B_BAD_DATA; - // "n" out in the middle of nowhere!? + throw ParseException(pos, "Unexpected 'n'"); break; } case '"': if (builder.What() != JSON_TYPE_ARRAY && key.Length() == 0) - key = _Parser_ParseString(JSON, pos); + key = _ParseString(JSON, pos); else if (builder.What() != JSON_TYPE_ARRAY && key.Length() > 0) { - builder.AddString(key, _Parser_ParseString(JSON, pos)); + builder.AddString(key, _ParseString(JSON, pos)); key = ""; } else if (builder.What() == JSON_TYPE_ARRAY) { key << builder.CountNames(); - builder.AddString(key, _Parser_ParseString(JSON, pos)); + builder.AddString(key, _ParseString(JSON, pos)); key = ""; } else - return B_BAD_DATA; - // Pretty sure it's impossible to get here, but you never know + throw ParseException(pos, "Internal error at encountering \""); break; @@ -174,14 +234,15 @@ BJson::Parse(BMessage& message, BString& JSON) case '8': case '9': { - if (builder.What() != JSON_TYPE_ARRAY && key.Length() == 0) - return B_BAD_DATA; - // Numbers cannot be keys, they can only be values + if (builder.What() != JSON_TYPE_ARRAY && key.Length() == 0) { + throw ParseException(pos, + "Numbers cannot be keys, they can only be values"); + } if (builder.What() == JSON_TYPE_ARRAY) key << builder.CountNames(); - double number = _Parser_ParseNumber(JSON, pos); + double number = _ParseNumber(JSON, pos); builder.AddDouble(key.String(), number); key = ""; @@ -197,16 +258,12 @@ BJson::Parse(BMessage& message, BString& JSON) pos++; } - // Reached the end of the BString without reaching the end of the document - return B_BAD_DATA; + throw ParseException(pos, "Unexpected end of document"); } -// #pragma mark - Private methods - - BString -BJson::_Parser_ParseString(BString& JSON, int32& pos) +BJson::_ParseString(BString& JSON, int32& pos) { if (JSON[pos] != '"') // Verify we're at the start of a string. return BString(""); @@ -270,7 +327,7 @@ BJson::_Parser_ParseString(BString& JSON, int32& pos) double -BJson::_Parser_ParseNumber(BString& JSON, int32& pos) +BJson::_ParseNumber(BString& JSON, int32& pos) { BString value; @@ -309,7 +366,7 @@ BJson::_Parser_ParseNumber(BString& JSON, int32& pos) bool -BJson::_Parser_ParseConstant(BString& JSON, int32& pos, const char* constant) +BJson::_ParseConstant(BString& JSON, int32& pos, const char* constant) { BString value; JSON.CopyInto(value, pos, strlen(constant));