| Index: base/json_reader.cc
|
| ===================================================================
|
| --- base/json_reader.cc (revision 6390)
|
| +++ base/json_reader.cc (working copy)
|
| @@ -71,66 +71,116 @@
|
|
|
| } // anonymous namespace
|
|
|
| +const char* JSONReader::kBadRootElementType =
|
| + "Root value must be an array or object.";
|
| +const char* JSONReader::kInvalidEscape =
|
| + "Invalid escape sequence.";
|
| +const char* JSONReader::kSyntaxError =
|
| + "Syntax error.";
|
| +const char* JSONReader::kTrailingComma =
|
| + "Trailing comma not allowed.";
|
| +const char* JSONReader::kTooMuchNesting =
|
| + "Too much nesting.";
|
| +const char* JSONReader::kUnexpectedDataAfterRoot =
|
| + "Unexpected data after root element.";
|
| +const char* JSONReader::kUnsupportedEncoding =
|
| + "Unsupported encoding. JSON must be UTF-8.";
|
| +const char* JSONReader::kUnquotedDictionaryKey =
|
| + "Dictionary keys must be quoted.";
|
| +
|
| /* static */
|
| bool JSONReader::Read(const std::string& json,
|
| Value** root,
|
| bool allow_trailing_comma) {
|
| - return JsonToValue(json, root, true, allow_trailing_comma);
|
| + return ReadAndReturnError(json, root, allow_trailing_comma, NULL);
|
| }
|
|
|
| /* static */
|
| -bool JSONReader::JsonToValue(const std::string& json,
|
| - Value** root,
|
| - bool check_root,
|
| - bool allow_trailing_comma) {
|
| +bool JSONReader::ReadAndReturnError(const std::string& json,
|
| + Value** root,
|
| + bool allow_trailing_comma,
|
| + std::string *error_message_out) {
|
| + JSONReader reader = JSONReader();
|
| + if (reader.JsonToValue(json, root, true, allow_trailing_comma)) {
|
| + return true;
|
| + } else {
|
| + if (error_message_out)
|
| + *error_message_out = *reader.error_message();
|
| + return false;
|
| + }
|
| +}
|
| +
|
| +/* static */
|
| +std::string JSONReader::FormatErrorMessage(int line, int column,
|
| + const char* description) {
|
| + return StringPrintf("Line: %i, column: %i, %s",
|
| + line, column, description);
|
| +}
|
| +
|
| +JSONReader::JSONReader()
|
| + : start_pos_(NULL), json_pos_(NULL), stack_depth_(0),
|
| + allow_trailing_comma_(false) {}
|
| +
|
| +bool JSONReader::JsonToValue(const std::string& json, Value** root,
|
| + bool check_root, bool allow_trailing_comma) {
|
| // The input must be in UTF-8.
|
| - if (!IsStringUTF8(json.c_str()))
|
| + if (!IsStringUTF8(json.c_str())) {
|
| + error_message_ = kUnsupportedEncoding;
|
| return false;
|
| + }
|
| +
|
| // The conversion from UTF8 to wstring removes null bytes for us
|
| // (a good thing).
|
| std::wstring json_wide(UTF8ToWide(json));
|
| - const wchar_t* json_cstr = json_wide.c_str();
|
| + start_pos_ = json_wide.c_str();
|
|
|
| // When the input JSON string starts with a UTF-8 Byte-Order-Mark
|
| // (0xEF, 0xBB, 0xBF), the UTF8ToWide() function converts it to a Unicode
|
| // BOM (U+FEFF). To avoid the JSONReader::BuildValue() function from
|
| // mis-treating a Unicode BOM as an invalid character and returning false,
|
| // skip a converted Unicode BOM if it exists.
|
| - if (!json_wide.empty() && json_cstr[0] == 0xFEFF) {
|
| - ++json_cstr;
|
| + if (!json_wide.empty() && start_pos_[0] == 0xFEFF) {
|
| + ++start_pos_;
|
| }
|
|
|
| - JSONReader reader(json_cstr, allow_trailing_comma);
|
| + json_pos_ = start_pos_;
|
| + allow_trailing_comma_ = allow_trailing_comma;
|
| + stack_depth_ = 0;
|
| + error_message_.clear();
|
|
|
| Value* temp_root = NULL;
|
| - bool success = reader.BuildValue(&temp_root, check_root);
|
|
|
| // Only modify root_ if we have valid JSON and nothing else.
|
| - if (success && reader.ParseToken().type == Token::END_OF_INPUT) {
|
| - *root = temp_root;
|
| - return true;
|
| + if (BuildValue(&temp_root, check_root)) {
|
| + if (ParseToken().type == Token::END_OF_INPUT) {
|
| + *root = temp_root;
|
| + return true;
|
| + } else {
|
| + SetErrorMessage(kUnexpectedDataAfterRoot, json_pos_);
|
| + }
|
| }
|
|
|
| + // Default to calling errors "syntax errors".
|
| + if (error_message_.empty())
|
| + SetErrorMessage(kSyntaxError, json_pos_);
|
| +
|
| if (temp_root)
|
| delete temp_root;
|
| return false;
|
| }
|
|
|
| -JSONReader::JSONReader(const wchar_t* json_start_pos,
|
| - bool allow_trailing_comma)
|
| - : json_pos_(json_start_pos),
|
| - stack_depth_(0),
|
| - allow_trailing_comma_(allow_trailing_comma) {}
|
| -
|
| bool JSONReader::BuildValue(Value** node, bool is_root) {
|
| ++stack_depth_;
|
| - if (stack_depth_ > kStackLimit)
|
| + if (stack_depth_ > kStackLimit) {
|
| + SetErrorMessage(kTooMuchNesting, json_pos_);
|
| return false;
|
| + }
|
|
|
| Token token = ParseToken();
|
| // The root token must be an array or an object.
|
| if (is_root && token.type != Token::OBJECT_BEGIN &&
|
| token.type != Token::ARRAY_BEGIN) {
|
| + SetErrorMessage(kBadRootElementType, json_pos_);
|
| return false;
|
| }
|
|
|
| @@ -184,6 +234,7 @@
|
| // consumers need the parsing leniency, so handle accordingly.
|
| if (token.type == Token::ARRAY_END) {
|
| if (!allow_trailing_comma_) {
|
| + SetErrorMessage(kTrailingComma, json_pos_);
|
| delete array;
|
| return false;
|
| }
|
| @@ -212,6 +263,7 @@
|
| DictionaryValue* dict = new DictionaryValue;
|
| while (token.type != Token::OBJECT_END) {
|
| if (token.type != Token::STRING) {
|
| + SetErrorMessage(kUnquotedDictionaryKey, json_pos_);
|
| delete dict;
|
| return false;
|
| }
|
| @@ -252,6 +304,7 @@
|
| // consumers need the parsing leniency, so handle accordingly.
|
| if (token.type == Token::OBJECT_END) {
|
| if (!allow_trailing_comma_) {
|
| + SetErrorMessage(kTrailingComma, json_pos_);
|
| delete dict;
|
| return false;
|
| }
|
| @@ -347,12 +400,16 @@
|
| // Make sure the escaped char is valid.
|
| switch (c) {
|
| case 'x':
|
| - if (!ReadHexDigits(token, 2))
|
| + if (!ReadHexDigits(token, 2)) {
|
| + SetErrorMessage(kInvalidEscape, json_pos_ + token.length);
|
| return kInvalidToken;
|
| + }
|
| break;
|
| case 'u':
|
| - if (!ReadHexDigits(token, 4))
|
| + if (!ReadHexDigits(token, 4)) {
|
| + SetErrorMessage(kInvalidEscape, json_pos_ + token.length);
|
| return kInvalidToken;
|
| + }
|
| break;
|
| case '\\':
|
| case '/':
|
| @@ -365,6 +422,7 @@
|
| case '"':
|
| break;
|
| default:
|
| + SetErrorMessage(kInvalidEscape, json_pos_ + token.length);
|
| return kInvalidToken;
|
| }
|
| } else if ('"' == c) {
|
| @@ -582,3 +640,25 @@
|
| return true;
|
| }
|
|
|
| +void JSONReader::SetErrorMessage(const char* description,
|
| + const wchar_t* error_pos) {
|
| + int line_number = 1;
|
| + int column_number = 1;
|
| +
|
| + // Figure out the line and column the error occured at.
|
| + for (const wchar_t* pos = start_pos_; pos != error_pos; ++pos) {
|
| + if (*pos == '\0') {
|
| + NOTREACHED();
|
| + return;
|
| + }
|
| +
|
| + if (*pos == '\n') {
|
| + ++line_number;
|
| + column_number = 1;
|
| + } else {
|
| + ++column_number;
|
| + }
|
| + }
|
| +
|
| + error_message_ = FormatErrorMessage(line_number, column_number, description);
|
| +}
|
|
|