| // 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. |
| |
| #include "json_parser.h" |
| |
| #include "json.h" |
| #include "test.h" |
| |
| namespace { |
| |
| const char* const kWhitespacePrefixes[] = { |
| "", " ", "\t\t", "\n", "\r\n\t ", |
| }; |
| |
| } // namespace |
| |
| TEST(JsonParserTest, PeekToken) { |
| const struct { |
| const char* input; |
| JsonParser::TokenType expected_type; |
| StringPiece expected_token; |
| } kTestCases[] = { |
| { "", JsonParser::kEndOfInput, "" }, |
| { "true", JsonParser::kTrue, "true" }, |
| { "true", JsonParser::kTrue, "true" }, |
| { "true", JsonParser::kTrue, "true" }, |
| { "false", JsonParser::kFalse, "false" }, |
| { "null", JsonParser::kNull, "null" }, |
| { "{", JsonParser::kObjectStart, "{" }, |
| { "{...", JsonParser::kObjectStart, "{" }, |
| { "}...", JsonParser::kObjectEnd, "}" }, |
| { "[...", JsonParser::kArrayStart, "[" }, |
| { "]...", JsonParser::kArrayEnd, "]" }, |
| { ",]", JsonParser::kComma, "," }, |
| { ":", JsonParser::kColon, ":" }, |
| { "\"foo\"", JsonParser::kString, "foo" }, |
| { "\"foo\\u0020bar\"", JsonParser::kString, "foo\\u0020bar" }, |
| { "!", JsonParser::kParsingError, "" }, |
| }; |
| for (const char* whitespace_prefix : kWhitespacePrefixes) { |
| for (const auto& test_case : kTestCases) { |
| std::string input = std::string(whitespace_prefix) + test_case.input; |
| JsonParser parser(input); |
| std::string err_message = "For input [" + input + "]"; |
| EXPECT_EQ(parser.PeekTokenType(), test_case.expected_type) << err_message; |
| EXPECT_EQ(parser.PeekToken(), test_case.expected_token) << err_message; |
| } |
| } |
| } |
| |
| TEST(JsonParserTest, Array) { |
| static const char kInput[] = |
| " [ \"some\\n\", \"newline\\n\", true, { \"foo\": [ \"bar\" ] } ]"; |
| |
| JsonParser parser(kInput); |
| ASSERT_TRUE(parser.ExpectArrayStart()); |
| StringPiece str_value; |
| ASSERT_TRUE(parser.ExpectEncodedString(&str_value)); |
| EXPECT_EQ(str_value, "some\\n"); |
| ASSERT_TRUE(parser.ExpectComma()); |
| std::string str; |
| ASSERT_TRUE(parser.ExpectString(&str)); |
| EXPECT_EQ(str, "newline\n"); |
| ASSERT_TRUE(parser.ExpectComma()); |
| bool bool_value = false; |
| ASSERT_TRUE(parser.ExpectBoolean(&bool_value)); |
| EXPECT_TRUE(bool_value); |
| ASSERT_TRUE(parser.ExpectComma()); |
| ASSERT_TRUE(parser.ExpectObjectStart()); |
| ASSERT_TRUE(parser.ExpectEncodedString(&str_value)); |
| EXPECT_EQ(str_value, "foo"); |
| ASSERT_TRUE(parser.ExpectColon()); |
| ASSERT_TRUE(parser.ExpectArrayStart()); |
| ASSERT_TRUE(parser.ExpectEncodedString(&str_value)); |
| EXPECT_EQ(str_value, "bar"); |
| ASSERT_TRUE(parser.ExpectArrayEnd()); |
| ASSERT_TRUE(parser.ExpectObjectEnd()); |
| ASSERT_TRUE(parser.ExpectArrayEnd()); |
| } |
| |
| TEST(JsonParserTest, SkipJsonValue) { |
| static const char kInput[] = |
| " [ \"some\", \"array\", true, { \"foo\": [ \"bar\" ] } ]"; |
| |
| JsonParser parser(kInput); |
| ASSERT_TRUE(parser.ExpectArrayStart()); |
| ASSERT_EQ(parser.PeekTokenType(), parser.kString); |
| ASSERT_TRUE(parser.SkipJsonValue()); |
| ASSERT_TRUE(parser.ExpectComma()); |
| ASSERT_EQ(parser.PeekTokenType(), parser.kString); |
| ASSERT_TRUE(parser.SkipJsonValue()); |
| ASSERT_TRUE(parser.ExpectComma()); |
| ASSERT_EQ(parser.PeekTokenType(), parser.kTrue); |
| ASSERT_TRUE(parser.SkipJsonValue()); |
| ASSERT_TRUE(parser.ExpectComma()); |
| ASSERT_EQ(parser.PeekTokenType(), parser.kObjectStart); |
| ASSERT_TRUE(parser.SkipJsonValue()); |
| ASSERT_TRUE(parser.ExpectArrayEnd()); |
| } |
| |
| TEST(JsonParserTest, ParseJsonValue) { |
| static const char kInput[] = |
| " [ \"some\", \"array\", true, { \"foo\": [ \"bar\" ] } ]"; |
| |
| JsonParser parser(kInput); |
| JsonParser::ValueInfo vi; |
| |
| ASSERT_TRUE(parser.ExpectArrayStart()); |
| ASSERT_TRUE(parser.ParseJsonValue(&vi)); |
| EXPECT_EQ(vi.type, JsonParser::kString); |
| EXPECT_EQ(vi.value, "some"); |
| ASSERT_TRUE(parser.ExpectComma()); |
| ASSERT_TRUE(parser.ParseJsonValue(&vi)); |
| EXPECT_EQ(vi.type, JsonParser::kString); |
| EXPECT_EQ(vi.value, "array"); |
| ASSERT_TRUE(parser.ExpectComma()); |
| ASSERT_TRUE(parser.ParseJsonValue(&vi)); |
| EXPECT_EQ(vi.type, JsonParser::kTrue); |
| EXPECT_EQ(vi.value, "true"); |
| ASSERT_TRUE(parser.ExpectComma()); |
| ASSERT_TRUE(parser.ParseJsonValue(&vi)); |
| EXPECT_EQ(vi.type, JsonParser::kObjectStart); |
| EXPECT_EQ(vi.value, "{ \"foo\": [ \"bar\" ] }"); |
| ASSERT_TRUE(parser.ExpectArrayEnd()); |
| } |
| |
| TEST(JsonParserTest, EmptyArrayIterator) { |
| JsonParser parser("[]"); |
| JsonParser::ArrayIterator it(parser); |
| ASSERT_EQ(parser.PeekTokenType(), parser.kArrayEnd); |
| ASSERT_FALSE(parser.HasError()); |
| ASSERT_FALSE(it.HasItem()); |
| ASSERT_FALSE(parser.HasError()); |
| ASSERT_EQ(parser.PeekTokenType(), parser.kEndOfInput); |
| } |
| |
| TEST(JsonParserTest, ExpectStringList) { |
| // Test with a valid string list. |
| { |
| JsonParser parser(R"([ "a", "b", "c" ])"); |
| std::vector<std::string> list; |
| ASSERT_TRUE(parser.ExpectStringList(&list)); |
| ASSERT_EQ(list.size(), 3u); |
| EXPECT_EQ(list[0], "a"); |
| EXPECT_EQ(list[1], "b"); |
| EXPECT_EQ(list[2], "c"); |
| } |
| |
| // Test with an empty list. |
| { |
| JsonParser parser("[]"); |
| std::vector<std::string> list; |
| ASSERT_TRUE(parser.ExpectStringList(&list)); |
| EXPECT_TRUE(list.empty()); |
| } |
| |
| // Test with a list containing non-string elements. |
| { |
| JsonParser parser(R"([ "a", 1, "c" ])"); |
| std::vector<std::string> list; |
| ASSERT_FALSE(parser.ExpectStringList(&list)); |
| EXPECT_TRUE(parser.HasError()); |
| } |
| |
| // Test with malformed JSON. |
| { |
| JsonParser parser("["); |
| std::vector<std::string> list; |
| ASSERT_FALSE(parser.ExpectStringList(&list)); |
| EXPECT_TRUE(parser.HasError()); |
| } |
| } |
| |
| TEST(JsonParserTest, ArrayIterator) { |
| static const char kInput[] = |
| " [ \"some\", \"array\", true, { \"foo\": [ \"bar\" ] } ]"; |
| |
| JsonParser parser(kInput); |
| JsonParser::ArrayIterator it(parser); |
| |
| ASSERT_TRUE(it.HasItem()); |
| StringPiece str_value; |
| ASSERT_TRUE(parser.ExpectEncodedString(&str_value)); |
| EXPECT_EQ(str_value, "some"); |
| |
| ASSERT_TRUE(it.HasItem()); |
| ASSERT_TRUE(parser.ExpectEncodedString(&str_value)); |
| EXPECT_EQ(str_value, "array"); |
| |
| ASSERT_TRUE(it.HasItem()); |
| bool bool_value = false; |
| ASSERT_TRUE(parser.ExpectBoolean(&bool_value)); |
| EXPECT_TRUE(bool_value); |
| |
| ASSERT_TRUE(it.HasItem()); |
| ASSERT_EQ(parser.PeekTokenType(), parser.kObjectStart); |
| ASSERT_TRUE(parser.SkipJsonValue()); |
| |
| ASSERT_FALSE(it.HasItem()); |
| ASSERT_FALSE(parser.HasError()); |
| ASSERT_EQ(parser.PeekTokenType(), parser.kEndOfInput); |
| } |
| |
| TEST(JsonParserTest, EmptyObjecttIterator) { |
| JsonParser parser("{}"); |
| JsonParser::ObjectIterator it(parser); |
| ASSERT_FALSE(parser.HasError()); |
| ASSERT_EQ(parser.PeekTokenType(), parser.kObjectEnd); |
| ASSERT_FALSE(it.HasKeyValue()); |
| ASSERT_EQ(parser.PeekTokenType(), parser.kEndOfInput); |
| } |
| |
| TEST(JsonParserTest, ObjectIterator) { |
| static const char kInput[] = |
| "{ \"a\" : \"AA\",\n \"b\" : [ 1, 2, 3 ], \"c\": { }, \"d\": false }"; |
| |
| JsonParser parser(kInput); |
| JsonParser::ObjectIterator it(parser); |
| |
| ASSERT_TRUE(it.HasKeyValue()); |
| ASSERT_EQ(it.key(), "a"); |
| StringPiece str_value; |
| ASSERT_TRUE(parser.ExpectEncodedString(&str_value)); |
| EXPECT_EQ(str_value, "AA"); |
| |
| ASSERT_TRUE(it.HasKeyValue()); |
| ASSERT_EQ(it.key(), "b"); |
| ASSERT_EQ(parser.PeekTokenType(), parser.kArrayStart); |
| ASSERT_TRUE(parser.SkipJsonValue()); |
| |
| ASSERT_TRUE(it.HasKeyValue()); |
| ASSERT_EQ(it.key(), "c"); |
| ASSERT_EQ(parser.PeekTokenType(), parser.kObjectStart); |
| ASSERT_TRUE(parser.SkipJsonValue()); |
| |
| ASSERT_TRUE(it.HasKeyValue()); |
| ASSERT_EQ(it.key(), "d"); |
| bool bool_value = true; |
| ASSERT_TRUE(parser.ExpectBoolean(&bool_value)); |
| EXPECT_FALSE(bool_value); |
| |
| ASSERT_FALSE(it.HasKeyValue()); |
| ASSERT_FALSE(parser.HasError()); |
| } |
| |
| TEST(JsonParserTest, ParseObject) { |
| // Input value is not an object is an error. |
| { |
| JsonParser parser("[]"); |
| JsonParser::KeyValueMap kv; |
| ASSERT_FALSE(parser.ParseObject(&kv)); |
| EXPECT_EQ(parser.error(), "expecting object start"); |
| } |
| // An empty object is not an error. |
| { |
| JsonParser parser("{}"); |
| JsonParser::KeyValueMap kv; |
| ASSERT_TRUE(parser.ParseObject(&kv)); |
| EXPECT_TRUE(kv.empty()); |
| } |
| // A single key value pair. |
| { |
| JsonParser parser(" { \"foo\": [ \"bar\" ] } "); |
| JsonParser::KeyValueMap kv; |
| ASSERT_TRUE(parser.ParseObject(&kv)); |
| EXPECT_EQ(kv.size(), 1u); |
| auto it = kv.begin(); |
| EXPECT_EQ(it->first, "foo"); |
| EXPECT_EQ(it->second.type, JsonParser::kArrayStart); |
| EXPECT_EQ(it->second.value, "[ \"bar\" ]"); |
| } |
| // Several key value pairs |
| { |
| JsonParser parser(" { \"foo\": [ \"bar\" ], \"zoo\": { \"a\": 42 } }"); |
| JsonParser::KeyValueMap kv; |
| ASSERT_TRUE(parser.ParseObject(&kv)); |
| EXPECT_EQ(kv.size(), 2u); |
| |
| JsonParser::ValueInfo vi; |
| ASSERT_TRUE(kv.Get("foo", &vi)); |
| EXPECT_EQ(vi.type, JsonParser::kArrayStart); |
| EXPECT_EQ(vi.value, "[ \"bar\" ]"); |
| ASSERT_TRUE(kv.Get("zoo", &vi)); |
| EXPECT_EQ(vi.type, JsonParser::kObjectStart); |
| EXPECT_EQ(vi.value, "{ \"a\": 42 }"); |
| EXPECT_FALSE(kv.Get("bar", &vi)); |
| } |
| // Malformed objects. |
| const char* kMalformedInputs[] = { |
| "{", |
| "}", |
| "{ \"a\" }", |
| "{ \"a\": }", |
| "{ : 1 }", |
| "{ \"a\": 1, }", |
| "{ \"a\": 1, , \"b\": 2 }", |
| "{ \"a\" 1 }", |
| }; |
| for (const char* input : kMalformedInputs) { |
| JsonParser parser(input); |
| JsonParser::KeyValueMap kv; |
| ASSERT_FALSE(parser.ParseObject(&kv)) << "input: " << input; |
| EXPECT_TRUE(parser.HasError()) << "input: " << input; |
| } |
| |
| // Duplicate keys. Last one wins. |
| { |
| JsonParser parser("{ \"a\": 1, \"b\": 2, \"a\": 3 }"); |
| JsonParser::KeyValueMap kv; |
| ASSERT_TRUE(parser.ParseObject(&kv)); |
| EXPECT_EQ(kv.size(), 2u); |
| JsonParser::ValueInfo vi; |
| ASSERT_TRUE(kv.Get("a", &vi)); |
| EXPECT_EQ(vi.type, JsonParser::kNumber); |
| EXPECT_EQ(vi.value, "3"); |
| ASSERT_TRUE(kv.Get("b", &vi)); |
| EXPECT_EQ(vi.type, JsonParser::kNumber); |
| EXPECT_EQ(vi.value, "2"); |
| } |
| } |
| |
| TEST(JsonParserTest, KeyValueMap) { |
| const char kInput[] = R"({ |
| "s": "string value", |
| "i": 123, |
| "b": true, |
| "n": null, |
| "a": [ "foo", "bar" ], |
| "o": { "x": 1 } |
| })"; |
| JsonParser parser(kInput); |
| JsonParser::KeyValueMap kv; |
| ASSERT_TRUE(parser.ParseObject(&kv)); |
| ASSERT_FALSE(parser.HasError()); |
| |
| JsonParser::ValueInfo vi; |
| EXPECT_TRUE(kv.Get("s", &vi)); |
| EXPECT_EQ(vi.type, JsonParser::kString); |
| EXPECT_FALSE(kv.Get("missing", &vi)); |
| |
| EXPECT_TRUE(kv.GetRequired("s", &vi, parser)); |
| EXPECT_FALSE(kv.GetRequired("missing", &vi, parser)); |
| EXPECT_EQ(parser.error(), "Missing required key 'missing'"); |
| parser.Reset(kInput); |
| |
| EXPECT_TRUE(kv.GetRequiredExpected("s", JsonParser::kString, &vi, parser)); |
| EXPECT_FALSE(kv.GetRequiredExpected("s", JsonParser::kNumber, &vi, parser)); |
| EXPECT_EQ(parser.error(), "Invalid type for key 's', expected NUMBER"); |
| parser.Reset(kInput); |
| |
| int64_t int_val; |
| EXPECT_TRUE(kv.GetRequiredInteger("i", &int_val, parser)); |
| EXPECT_EQ(int_val, 123); |
| EXPECT_FALSE(kv.GetRequiredInteger("s", &int_val, parser)); |
| EXPECT_EQ(parser.error(), "Invalid type for key 's', expected NUMBER"); |
| parser.Reset(kInput); |
| |
| std::string str_val; |
| EXPECT_TRUE(kv.GetRequiredString("s", &str_val, parser)); |
| EXPECT_EQ(str_val, "string value"); |
| EXPECT_FALSE(kv.GetRequiredString("i", &str_val, parser)); |
| EXPECT_EQ(parser.error(), "Invalid type for key 'i', expected STRING"); |
| parser.Reset(kInput); |
| |
| std::vector<std::string> list_val; |
| EXPECT_TRUE(kv.GetRequiredStringList("a", &list_val, parser)); |
| EXPECT_EQ(list_val.size(), 2u); |
| EXPECT_EQ(list_val[0], "foo"); |
| EXPECT_EQ(list_val[1], "bar"); |
| EXPECT_FALSE(kv.GetRequiredStringList("s", &list_val, parser)); |
| EXPECT_EQ(parser.error(), "Invalid type for key 's', expected ARRAY_START"); |
| parser.Reset(kInput); |
| } |
| |
| TEST(JsonParserTest, JsonInputStringInfo) { |
| { |
| StringPiece input("missing starting quote\""); |
| auto info = JsonParser::ParseJsonInputString(input.begin(), input.end()); |
| EXPECT_EQ(info.start, input.begin()); |
| EXPECT_EQ(info.end, input.end()); |
| EXPECT_TRUE(info.error); |
| } |
| { |
| StringPiece input("\"missing closing quote"); |
| auto info = JsonParser::ParseJsonInputString(input.begin(), input.end()); |
| EXPECT_EQ(info.start, input.begin() + 1); |
| EXPECT_EQ(info.end, input.end()); |
| EXPECT_TRUE(info.error); |
| } |
| { |
| StringPiece input("\"simple string without escapes\" extra"); |
| auto info = JsonParser::ParseJsonInputString(input.begin(), input.end()); |
| EXPECT_EQ(info.start, input.begin() + 1); |
| EXPECT_EQ(info.end, input.end() - 7u); |
| EXPECT_FALSE(info.error); |
| } |
| { |
| StringPiece input("\"string with escaped quote \\\" here\" extra"); |
| auto info = JsonParser::ParseJsonInputString(input.begin(), input.end()); |
| EXPECT_EQ(info.start, input.begin() + 1); |
| EXPECT_EQ(info.end, input.end() - 7u); |
| EXPECT_FALSE(info.error); |
| } |
| { |
| StringPiece input("\"string with escaped backslash \\\\\" extra"); |
| auto info = JsonParser::ParseJsonInputString(input.begin(), input.end()); |
| EXPECT_EQ(info.start, input.begin() + 1); |
| EXPECT_EQ(info.end, input.end() - 7u); |
| EXPECT_FALSE(info.error); |
| } |
| { |
| StringPiece input("\"string with unicode escape \\u0022\" extra"); |
| auto info = JsonParser::ParseJsonInputString(input.begin(), input.end()); |
| EXPECT_EQ(info.start, input.begin() + 1); |
| EXPECT_EQ(info.end, input.end() - 7u); |
| EXPECT_FALSE(info.error); |
| } |
| { |
| StringPiece input( |
| "\"string with unicode escape (surrogate pair) \\uD800\\uDC00\" extra"); |
| auto info = JsonParser::ParseJsonInputString(input.begin(), input.end()); |
| EXPECT_EQ(info.start, input.begin() + 1); |
| EXPECT_EQ(info.end, input.end() - 7u); |
| EXPECT_FALSE(info.error); |
| } |
| { |
| StringPiece input( |
| "\"string with misc escapes \\b\\f\\r\\n\\t\\\\\\\"\\u0022\" extra"); |
| auto info = JsonParser::ParseJsonInputString(input.begin(), input.end()); |
| EXPECT_EQ(info.start, input.begin() + 1); |
| EXPECT_EQ(info.end, input.end() - 7u); |
| EXPECT_FALSE(info.error); |
| } |
| { |
| StringPiece input("\"broken unicode escape 1 \\u022 \""); |
| auto info = JsonParser::ParseJsonInputString(input.begin(), input.end()); |
| EXPECT_EQ(info.start, input.begin() + 1); |
| EXPECT_EQ(info.end, input.end() - 7u); |
| EXPECT_TRUE(info.error); |
| } |
| { |
| StringPiece input("\"broken unicode escape 2 \\u22 \""); |
| auto info = JsonParser::ParseJsonInputString(input.begin(), input.end()); |
| EXPECT_EQ(info.start, input.begin() + 1); |
| EXPECT_EQ(info.end, input.end() - 6u); |
| EXPECT_TRUE(info.error); |
| } |
| { |
| StringPiece input("\"broken unicode escape 3 \\u \""); |
| auto info = JsonParser::ParseJsonInputString(input.begin(), input.end()); |
| EXPECT_EQ(info.start, input.begin() + 1); |
| EXPECT_EQ(info.end, input.end() - 4u); |
| EXPECT_TRUE(info.error); |
| } |
| { |
| StringPiece input("\"broken unicode escape 4 \\uX022\""); |
| auto info = JsonParser::ParseJsonInputString(input.begin(), input.end()); |
| EXPECT_EQ(info.start, input.begin() + 1); |
| EXPECT_EQ(info.end, input.end() - 7u); |
| EXPECT_TRUE(info.error); |
| } |
| { |
| StringPiece input( |
| "\"broken unicode escape 5 (surrogate pair start without following " |
| "end) " |
| "\\uDC00\""); |
| auto info = JsonParser::ParseJsonInputString(input.begin(), input.end()); |
| EXPECT_EQ(info.start, input.begin() + 1); |
| EXPECT_EQ(info.end, input.end() - 7u); |
| EXPECT_TRUE(info.error); |
| } |
| { |
| StringPiece input( |
| "\"broken unicode escape 6 (surrogate pair end doubled) " |
| "\\uDC00\\uDC00\""); |
| auto info = JsonParser::ParseJsonInputString(input.begin(), input.end()); |
| EXPECT_EQ(info.start, input.begin() + 1); |
| EXPECT_EQ(info.end, input.end() - 13u); |
| EXPECT_TRUE(info.error); |
| } |
| } |
| |
| TEST(JsonParserTest, DecodeString) { |
| static const struct { |
| const char* input; |
| const char* expected; |
| } kTestCases[] = { |
| { "", "" }, |
| { "foo bar", "foo bar" }, |
| { "\\\"hello\\\"", "\"hello\"" }, |
| { "hello\\nworld\\r\\n", "hello\nworld\r\n" }, |
| { "single\\bchar\\fescapes\\t", "single\bchar\fescapes\t" }, |
| { "\u0022hello\u0020world\u002f\u0022", "\"hello world/\"" }, |
| { "a \\ud800\\udc00 b", "a \xf4\x80\x80\x80 b" }, |
| }; |
| for (const auto& test_case : kTestCases) { |
| std::string actual = test_case.input; |
| std::string got = JsonParser::DecodeString(actual); |
| EXPECT_EQ(got, test_case.expected) << "For input " << actual; |
| } |
| } |