summaryrefslogtreecommitdiff
path: root/AK/JsonParser.cpp
diff options
context:
space:
mode:
authorMatthew Olsson <matthewcolsson@gmail.com>2020-06-10 21:40:27 -0700
committerAndreas Kling <kling@serenityos.org>2020-06-13 12:43:22 +0200
commite8e728454c5d436a02eaa1d24bc3afe357090c52 (patch)
treeac40614d78823eaba887e5657132681761c2b7cc /AK/JsonParser.cpp
parent39576b22385a2e6b6fc4fbf5e90e6b72157e9ee2 (diff)
downloadserenity-e8e728454c5d436a02eaa1d24bc3afe357090c52.zip
AK: JsonParser improvements
- Parsing invalid JSON no longer asserts Instead of asserting when coming across malformed JSON, JsonParser::parse now returns an Optional<JsonValue>. - Disallow trailing commas in JSON objects and arrays - No longer parse 'undefined', as that is a purely JS thing - No longer allow non-whitespace after anything consumed by the initial parse() call. Examples of things that were valid and no longer are: - undefineddfz - {"foo": 1}abcd - [1,2,3]4 - JsonObject.for_each_member now iterates in original insertion order
Diffstat (limited to 'AK/JsonParser.cpp')
-rw-r--r--AK/JsonParser.cpp120
1 files changed, 80 insertions, 40 deletions
diff --git a/AK/JsonParser.cpp b/AK/JsonParser.cpp
index 7a883a8b3a..142c797a47 100644
--- a/AK/JsonParser.cpp
+++ b/AK/JsonParser.cpp
@@ -62,15 +62,16 @@ void JsonParser::consume_whitespace()
consume_while([](char ch) { return is_whitespace(ch); });
}
-void JsonParser::consume_specific(char expected_ch)
+bool JsonParser::consume_specific(char expected_ch)
{
char consumed_ch = consume();
- ASSERT(consumed_ch == expected_ch);
+ return consumed_ch == expected_ch;
}
String JsonParser::consume_quoted_string()
{
- consume_specific('"');
+ if (!consume_specific('"'))
+ return {};
Vector<char, 1024> buffer;
for (;;) {
@@ -136,7 +137,8 @@ String JsonParser::consume_quoted_string()
break;
}
}
- consume_specific('"');
+ if (!consume_specific('"'))
+ return {};
if (buffer.is_empty())
return String::empty();
@@ -151,55 +153,78 @@ String JsonParser::consume_quoted_string()
return last_string_starting_with_character;
}
-JsonObject JsonParser::parse_object()
+Optional<JsonValue> JsonParser::parse_object()
{
JsonObject object;
- consume_specific('{');
+ if (!consume_specific('{'))
+ return {};
for (;;) {
consume_whitespace();
if (peek() == '}')
break;
consume_whitespace();
auto name = consume_quoted_string();
+ if (name.is_null())
+ return {};
consume_whitespace();
- consume_specific(':');
+ if (!consume_specific(':'))
+ return {};
consume_whitespace();
- auto value = parse();
- object.set(name, move(value));
+ auto value = parse_helper();
+ if (!value.has_value())
+ return {};
+ object.set(name, move(value.value()));
consume_whitespace();
if (peek() == '}')
break;
- consume_specific(',');
+ if (!consume_specific(','))
+ return {};
+ consume_whitespace();
+ if (peek() == '}')
+ return {};
}
- consume_specific('}');
+ if (!consume_specific('}'))
+ return {};
return object;
}
-JsonArray JsonParser::parse_array()
+Optional<JsonValue> JsonParser::parse_array()
{
JsonArray array;
- consume_specific('[');
+ if (!consume_specific('['))
+ return {};
for (;;) {
consume_whitespace();
if (peek() == ']')
break;
- array.append(parse());
+ auto element = parse_helper();
+ if (!element.has_value())
+ return {};
+ array.append(element.value());
consume_whitespace();
if (peek() == ']')
break;
- consume_specific(',');
+ if (!consume_specific(','))
+ return {};
+ consume_whitespace();
+ if (peek() == ']')
+ return {};
}
consume_whitespace();
- consume_specific(']');
+ if (!consume_specific(']'))
+ return {};
return array;
}
-JsonValue JsonParser::parse_string()
+Optional<JsonValue> JsonParser::parse_string()
{
- return consume_quoted_string();
+ auto result = consume_quoted_string();
+ if (result.is_null())
+ return {};
+ return JsonValue(result);
}
-JsonValue JsonParser::parse_number()
+Optional<JsonValue> JsonParser::parse_number()
{
JsonValue value;
Vector<char, 128> number_buffer;
@@ -235,7 +260,10 @@ JsonValue JsonParser::parse_number()
if (to_signed_result.has_value()) {
whole = to_signed_result.value();
} else {
- whole = number_string.to_int().value();
+ auto number = number_string.to_int();
+ if (!number.has_value())
+ return {};
+ whole = number.value();
}
int fraction = fraction_string.to_uint().value();
@@ -252,7 +280,10 @@ JsonValue JsonParser::parse_number()
if (to_unsigned_result.has_value()) {
value = JsonValue(to_unsigned_result.value());
} else {
- value = JsonValue(number_string.to_int().value());
+ auto number = number_string.to_int();
+ if (!number.has_value())
+ return {};
+ value = JsonValue(number.value());
}
#ifndef KERNEL
}
@@ -261,37 +292,37 @@ JsonValue JsonParser::parse_number()
return value;
}
-void JsonParser::consume_string(const char* str)
+bool JsonParser::consume_string(const char* str)
{
- for (size_t i = 0, length = strlen(str); i < length; ++i)
- consume_specific(str[i]);
+ for (size_t i = 0, length = strlen(str); i < length; ++i) {
+ if (!consume_specific(str[i]))
+ return false;
+ }
+ return true;
}
-JsonValue JsonParser::parse_true()
+Optional<JsonValue> JsonParser::parse_true()
{
- consume_string("true");
+ if (!consume_string("true"))
+ return {};
return JsonValue(true);
}
-JsonValue JsonParser::parse_false()
+Optional<JsonValue> JsonParser::parse_false()
{
- consume_string("false");
+ if (!consume_string("false"))
+ return {};
return JsonValue(false);
}
-JsonValue JsonParser::parse_null()
+Optional<JsonValue> JsonParser::parse_null()
{
- consume_string("null");
+ if (!consume_string("null"))
+ return {};
return JsonValue(JsonValue::Type::Null);
}
-JsonValue JsonParser::parse_undefined()
-{
- consume_string("undefined");
- return JsonValue(JsonValue::Type::Undefined);
-}
-
-JsonValue JsonParser::parse()
+Optional<JsonValue> JsonParser::parse_helper()
{
consume_whitespace();
auto type_hint = peek();
@@ -320,10 +351,19 @@ JsonValue JsonParser::parse()
return parse_true();
case 'n':
return parse_null();
- case 'u':
- return parse_undefined();
}
- return JsonValue();
+ return {};
+}
+
+Optional<JsonValue> JsonParser::parse() {
+ auto result = parse_helper();
+ if (!result.has_value())
+ return {};
+ consume_whitespace();
+ if (m_index != m_input.length())
+ return {};
+ return result;
}
+
}