| // Copyright 2025 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #pragma once |
| |
| #include <functional> |
| #include <string> |
| |
| #include "hash_map.h" |
| #include "string_piece.h" |
| #include "util.h" |
| |
| /// A minimalist JSON parser, designed for performance and simplicity, and as |
| /// such, with a few intentional limitations: |
| /// |
| /// - Error reporting is minimal and does not include line or column numbers. |
| /// This is a trade-off to keep the implementation simple and fast. |
| /// |
| /// - It uses StringPiece for all string-like tokens to avoid dynamic memory |
| /// allocations during parsing. The caller is responsible for ensuring the |
| /// underlying input buffer remains valid. |
| /// |
| /// - It is a streaming parser, and does not support random access. |
| /// |
| /// - It does not support comments (i.e. JSON, not JSON5). |
| /// |
| struct JsonParser { |
| /// A simple enum describing supported JSON token types. |
| enum TokenType { |
| kNone, |
| kEndOfInput, |
| kParsingError, |
| kObjectStart, |
| kObjectEnd, |
| kArrayStart, |
| kArrayEnd, |
| kComma, |
| kColon, |
| kString, |
| kTrue, |
| kFalse, |
| kNull, |
| kNumber, |
| }; |
| |
| /// Constructor takes input as a single StringPiece. |
| JsonParser(StringPiece input); |
| |
| /// Reset the state of the parser for new input, clearing the error. |
| void Reset(StringPiece input); |
| |
| /// Return true if an error was encountered during parsing. This |
| /// condition is sticky until the next Reset() call. |
| bool HasError() const { return !error_.empty(); } |
| |
| /// Return true if no error was encountered during parsing. |
| bool IsOk() const { return error_.empty(); } |
| |
| /// Return error string. This will be empty if no error occurred. |
| const std::string& error() const { return error_; } |
| |
| /// Take the error string, moving it out of the parser. State |
| /// is undefined after this call, but useful to pass the error |
| /// to the caller before returning. |
| std::string TakeError() { return std::move(error_); } |
| |
| /// Set the error message. This forces future PeekToken() to return |
| /// kParsingError, and future HasError() calls to return true. |
| /// Always return false to simplify error handling. |
| bool SetError(std::string error); |
| |
| /// A SetError() variant that can format arguments. |
| bool SetErrorFormat(const char* fmt, ...); |
| |
| /// Used internally to set the error in an ExpectXXX() method and return |
| /// false to simplify error handling. |
| bool SetExpectedError(const char* expected); |
| |
| /// Return type of current token. This will be kEndOfInput if there are no |
| /// more tokens from the input, or kParsingError if an error occurred. |
| TokenType PeekTokenType(); |
| |
| /// Return the current token as a StringPiece. This will be empty |
| /// if PeekTokenType() returns kEndOfInput or kParsingError. |
| StringPiece PeekToken(); |
| |
| /// Skip the current token. This is a no-op if an error occurred. |
| /// Useful after checking the current token type with PeekTokenType(). |
| void SkipToken(); |
| |
| /// Return a string representation of a given token. |
| static std::string ToString(TokenType type); |
| |
| /// Return true if the next token matches the start of a JSON value. |
| /// or false if it corresponds to an array end, object end, or malformed |
| /// input. |
| bool PeekValueStart(); |
| |
| /// Verify that the current token is the start of a JSON value, but |
| /// do not skip over it. If not, set an error and return false. |
| bool ExpectValueStartNoSkip(); |
| |
| /// For all ExpectXXXX() methods below, if the current token is of a certain |
| /// type, skip over it then return true. Otherwise, set error then return |
| /// false. |
| bool ExpectColon() { return ExpectTokenType(kColon, "expecting colon"); } |
| bool ExpectComma() { return ExpectTokenType(kComma, "expecting comma"); } |
| bool ExpectNull() { return ExpectTokenType(kNull, "expecting comma"); } |
| bool ExpectObjectStart() { |
| return ExpectTokenType(kObjectStart, "expecting object start"); |
| } |
| bool ExpectObjectEnd() { |
| return ExpectTokenType(kObjectEnd, "expecting object end"); |
| } |
| bool ExpectArrayStart() { |
| return ExpectTokenType(kArrayStart, "expecting array start"); |
| } |
| bool ExpectArrayEnd() { |
| return ExpectTokenType(kArrayEnd, "expecting array end"); |
| } |
| |
| /// If the current token is a number, set |*number| to its value, |
| /// skip over it then return true. Otherwise set error then return false. |
| bool ExpectNumber(StringPiece* number); |
| |
| /// If the current token is a decimal number, set |*int_value| to its value, |
| /// skip over it then return true. Otherwise set error then return false. |
| bool ExpectInteger(int64_t* int_value); |
| |
| /// If the current token is a floating point number, set |*double_value| |
| // to its value, skip over it then return true. Otherwise set error then |
| // return false. |
| bool ExpectDouble(double* double_value); |
| |
| /// If the current token is a boolean, set |*flag_value| to its value, |
| /// skip over it then return true. Otherwise set error then return false. |
| bool ExpectBoolean(bool* flag_value); |
| |
| /// If the current token is a string, set |*encoded_str| to its undecoded |
| /// value, without opening and closing quotes, skip over it, then return true. |
| /// Otherwise set error then return false. |
| bool ExpectEncodedString(StringPiece* encoded_str); |
| |
| /// Same as ExpectedEncodedString, but set |*str| to the decoded value. |
| bool ExpectString(std::string* str); |
| |
| /// Expect an array of strings. On success sets |*str_list| and return true. |
| /// On failure set parser error then return false. |
| bool ExpectStringList(std::vector<std::string>* str_list); |
| |
| /// If the current token is a string followed by a colon token, as required |
| /// for object keys, set |key_name| to the _encoded_ key name, skip over |
| /// both tokens then return true. Otherwise set error then return false. |
| bool ExpectObjectKeyAndColon(StringPiece* key_name) { |
| return ExpectEncodedString(key_name) && ExpectColon(); |
| } |
| |
| /// If the current three tokens are a string, a colon, and a string, then |
| /// set |*key| and |*value|, skip over the three tokens, then return true. |
| /// Otherwise set error then return false. Note that both |*key| and |*value| |
| /// are encoded. |
| bool ExpectObjectKeyAndStringValue(StringPiece* key, StringPiece* value) { |
| return ExpectObjectKeyAndColon(key) && ExpectEncodedString(value); |
| } |
| |
| /// Skip the current JSON value. This handles arrays and objects by calling |
| /// itself recursively. On success, return true. On failure, set error then |
| /// return false. It is an error if the current token does not start a value. |
| bool SkipJsonValue(); |
| |
| /// A struct to store the current JSON encoded value. |
| struct ValueInfo { |
| TokenType type = JsonParser::kNone; |
| StringPiece value; |
| }; |
| |
| /// Store the current JSON value into |*value| then skip it. |
| bool ParseJsonValue(ValueInfo* info); |
| |
| /// If the current token is "]", skip over it and return true. |
| /// Otherwise simply return false. |
| bool SkipOptionalArrayEnd(); |
| |
| /// If the current token is a comma, skip over it and return true. |
| /// Otherwise check that the current token is a "]" denoting an array end. |
| /// On success return true but do not skip it. On failure, set error |
| /// condition and return false. |
| bool CheckOptionalArrayComma(); |
| |
| /// If the current token is "}", sip over it and return true. |
| /// Otherwise simply return false. |
| bool SkipOptionalObjectEnd(); |
| |
| /// If the current token is a comma, skip over it and return true. |
| /// Otherwise check that the current token is a "}" denoting an object end. |
| /// On success return true but do not skip it. On failure, set error |
| /// condition and return false. |
| bool CheckOptionalObjectComma(); |
| |
| /// Helper class used to iterate over a JSON array value. Usage is: |
| /// |
| /// ArrayIterator it(parser); |
| /// while (it.HasItem()) { |
| /// .. read single item from |parser|. |
| /// } |
| /// return !parser.HasError(); // always check after the loop. |
| /// |
| /// For example, to read an array of strings: |
| /// |
| /// auto parse_string_list = [&parser]( |
| // std::vector<std::string>* result) -> bool { |
| /// ArrayIterator it(parser); |
| /// while (it.HasItem()) { |
| /// std::string item; |
| /// if (!parser.ExpectString(&item)) |
| /// return false; |
| /// result->push_back(std::move(item)); |
| /// } |
| /// return true; |
| /// }; |
| /// |
| struct ArrayIterator { |
| /// Constructor takes a mutable reference to a JsonParser object |
| /// that will be modified directly by calling HasItem(). |
| /// It is an error if the current token is not kArrayStart. |
| ArrayIterator(JsonParser& parser); |
| |
| /// Return true if there is one more item to read, and false |
| /// if the array has ended, or an error occurred. If the parser |
| /// passed to the constructor was not set to kArrayStart, then |
| /// the first call to HasItem() will return false. |
| bool HasItem(); |
| |
| private: |
| JsonParser& parser_; |
| bool expect_comma_ = false; |
| }; |
| |
| /// Helper class used to iterate over all (key, value) pairs in an object. |
| /// Users are encouraged to use JsonParser::ParseObject() instead which |
| /// keys appearing in any order in the input object, and delaying their |
| /// parsing until the value is needed. |
| /// |
| /// Usage is: |
| /// |
| /// ObjectIterator it(parser); |
| /// while (it.HasKeyValue()) { |
| /// ... Use it.key() to access the encoded key. |
| /// ... read single value from |parser| here. |
| /// } |
| /// return !parser.HasError(); // Always check after the loop. |
| // |
| struct ObjectIterator { |
| /// Constructor takes a mutable reference to a JsonParser instance |
| /// that will be modified by HasKeyValue() calls below. |
| /// |
| /// If the current token is not "{", then the first HasKeyValue() call |
| /// will return false, and HasError() will return true. |
| ObjectIterator(JsonParser& parser); |
| |
| /// Return true if there is a (key, value) pair to read. |
| /// On success, read the key and colon and leave the parser's |
| /// position on the token of the corresponding value, to be read |
| /// by the caller. The key can be retrieved after this call with key(). |
| bool HasKeyValue(); |
| |
| /// The encoded key corresponding to the latest successful HasKeyValue() |
| /// call. |
| StringPiece key() const { return key_; } |
| |
| private: |
| JsonParser& parser_; |
| StringPiece key_; |
| bool expect_comma_ = false; |
| }; |
| |
| /// A mapping from object key names to object values, as they appear in |
| /// the input. Used by ParseObject() method below. |
| /// |
| /// Note that to minimize memory allocations, this only uses StringPiece |
| /// for keys and for the returned encoded value (ValueInfo::value). The |
| /// caller is responsible for ensuring that the StringPiece values remain |
| /// valid for the lifetime of the KeyValueMap. |
| /// |
| /// Usage example is: |
| /// |
| /// // Parse a simple JSON object which expects the following schema: |
| /// // |
| /// // "version": Required. An integer that must be 1 or 2 |
| /// // "command": Required. A command string. |
| /// // "args": Required in version 1, and optional in version 2, |
| /// // an array of argument strings for the command. |
| /// // |
| /// // On success, set |*command| and |*args| then return true. |
| /// // On failure, set parser error then return false. |
| /// // |
| /// auto parse_my_object = [&parser](std::string* command, |
| /// std::vector<std::string>* args) -> bool { |
| /// KeyValueMap kv; |
| /// if (!parser.ParseObject(&kv)) |
| /// return false; |
| /// |
| /// int64_t version = 0; |
| /// if (!kv.GetRequiredInteger("version", &version, parser)) |
| /// return false; |
| /// |
| /// if (version != 1 && version != 2) { |
| /// parser.SetErrorFormat("Invalid version: %" PRId64, version); |
| /// return false; |
| /// } |
| /// |
| /// if (!kv.GetRequiredString("command", command, parser)) |
| /// return false; |
| /// |
| /// args->clear(); |
| /// ValueInfo vi; |
| /// bool has_args = kv.Get("args", &vi); |
| /// if (version == 1 && !has_args) { |
| /// return parser.SetError("Missing array for args"); |
| /// } |
| /// if (has_args) { |
| /// JsonParser subparser(vi.value); |
| /// if (!subparser.ExpectStringList(args)) |
| /// return parser.SetError(subparser.TakeError()); |
| /// } |
| /// // Done. |
| /// return true; |
| /// }; |
| /// |
| struct KeyValueMap : public ExternalStringHashMap<ValueInfo>::Type { |
| /// Retrieve the value associated with a given optional |key|. If the key |
| /// has a value, set |*vi| to it, then return true. If there |
| /// is no associated value, return false. |
| bool Get(StringPiece key, ValueInfo* vi) const; |
| |
| /// Retrieve the value associated with a given optional |key|, after |
| /// verifying its type. If the key has an associated value with the |
| /// right type, set |*vi| to it, then return true. If there is no |
| /// associated value, set |*vi| to an invalid value, then return |
| /// true as well. Otherwise, cal |parser.SetError()| then return false. |
| bool GetOptionalTyped(StringPiece key, TokenType expected_type, |
| StringPiece* value, JsonParser& parser) const; |
| |
| /// Retrieve the value associated with a given required |key|. On success, |
| /// set |*vi| then return true, on failure call |parser.SetError()| |
| /// then return false. |
| bool GetRequired(StringPiece key, ValueInfo* vi, JsonParser& parser) const; |
| |
| /// Same as GetRequired, but also errors if the returned token type |
| /// does not match |expected_type|. |
| bool GetRequiredExpected(StringPiece key, TokenType expected_type, |
| ValueInfo* vi, JsonParser& parser) const; |
| |
| /// Retrieve a required integer value associated with |key|. |
| bool GetRequiredInteger(StringPiece key, int64_t* value, |
| JsonParser& parser) const; |
| |
| /// Retrieve a required string value associated with |key|. On success |
| /// set |*value| to the decoded string value and return true. On failure |
| /// call |parser.SetError()| and return false. |
| bool GetRequiredString(StringPiece key, std::string* value, |
| JsonParser& parser) const; |
| |
| /// Retrieve a required array of strings associated with |key|. On success |
| /// set |*value| and return true. On failure, call |parser.SetError()| |
| /// then return false. |
| bool GetRequiredStringList(StringPiece key, std::vector<std::string>* value, |
| JsonParser& parser) const; |
| |
| /// Retrieve an optional string associated with |key|, or an empty string |
| /// otherwise. On success, set |*value| then return true. On failure |
| /// call |parser.SetError()| then return false. |
| bool GetOptionalOrEmptyString(StringPiece key, std::string* value, |
| JsonParser& parser) const; |
| }; |
| |
| /// Parse a JSON object into an ObjectKeyValueMap. |
| /// On success, set |*kv_map| then return true. |
| /// On failure, clear |*kv_map|, set parser error, then return false. |
| /// |
| /// Example usage: |
| /// |
| /// ObjectKeyValueMap key_values = parser.ParseObject(); |
| /// if (parser.HasError()) { |
| /// ... deal with error |
| /// } |
| /// StringPiece version; |
| /// if (!key_values.GetRequired("version", &version)) { |
| /// JsonParser subparser(version) |
| /// } |
| /// parser.SetError("Missing required key 'version'"); |
| bool ParseObject(KeyValueMap* kv_map); |
| |
| /// If [start,limit) begins with a JSON number expression, set |*end_ptr| |
| /// to the first character after it, then return true. Otherwise return false. |
| /// Exposed for unit-tests only. |
| static bool IsJsonNumber(const char* start, const char* limit, |
| const char** end_ptr); |
| |
| /// Helper function used to parse JSON input strings. |
| /// |start| points to the opening quote in the input. |
| /// |limit| points to the first character in memory after the input. |
| /// function is called. On success, set |*limit_ptr| to point to the closing |
| /// quote in the input, and return true. On failure, set |*limit_ptr| to the |
| /// first malformed input char from the input, and return false. |
| /// |
| /// The function does not try ton unquote the input string, only find its |
| /// closing quote position. Example usage: |
| /// |
| /// StringPiece json_input = ...; |
| /// if (json_input[0] == '"') { |
| /// auto str_info = ParseJsonInputString(json_input.begin(), |
| /// json_input.end()); if (str_info.error) { |
| /// // malformed input. return error. |
| /// } |
| /// std::string value = DecodeString(str_info.start, str_info.end - |
| /// str_info.start); |
| /// } |
| /// const char* token_start = ...; |
| /// const char* token_end = input.end(); |
| struct JsonInputStringInfo { |
| const char* start = nullptr; // pointer to first char after opening quote. |
| const char* end = nullptr; // pointer to closing quote, or first malformed |
| // character, or limit. |
| bool error = false; |
| }; |
| |
| static JsonInputStringInfo ParseJsonInputString(const char* start_quote, |
| const char* limit); |
| |
| /// Decode a string in JSON format into the corresponding UTF-8 |
| /// representation. This assumes the input is well-formed. Otherwise the |
| /// result value is not guaranteed to be valid. |
| static std::string DecodeString(StringPiece str); |
| |
| private: |
| bool ExpectTokenType(TokenType token_type, const char* error_msg); |
| |
| void EnsureToken() { |
| if (token_type_ == kNone) |
| ParseToken(); |
| } |
| |
| void ParseToken(); |
| |
| const char* p_ = nullptr; |
| const char* end_ = nullptr; |
| TokenType token_type_ = kNone; |
| StringPiece token_; |
| std::string error_; |
| }; |