From 436d9ecdb7606f2f25fe2beda9d42f77806ae918 Mon Sep 17 00:00:00 2001 From: Mikhail Lobotskiy Date: Wed, 4 Sep 2024 21:35:56 +0400 Subject: [PATCH] Implement arrays parsing in FromJSON --- DesktopEditor/doctrenderer/json/json.cpp | 95 ++++++-- DesktopEditor/doctrenderer/test/json/main.cpp | 211 +++++++++++++++++- 2 files changed, 279 insertions(+), 27 deletions(-) diff --git a/DesktopEditor/doctrenderer/json/json.cpp b/DesktopEditor/doctrenderer/json/json.cpp index 4218e35a07..3eb93fe432 100644 --- a/DesktopEditor/doctrenderer/json/json.cpp +++ b/DesktopEditor/doctrenderer/json/json.cpp @@ -612,7 +612,7 @@ namespace NSJSON namespace { - // moves pos to first non-space symbol and returns `true` if pos is still inside the string after moving + // moves pos to first non-space symbol and returns false if end of the string was reached bool skipWhitespaces(const std::string& str, int& pos) { for (; pos < str.size(); pos++) @@ -732,53 +732,93 @@ namespace NSJSON // if did not encounter closing qoutes, return undefined if (pos == str.size()) return CValue(); - pos++; + return result; } + // parse one value of JSON-string `str` starting from `pos` + CValue valueFromJSON(const std::string& str, int& pos); + CValue parseArrayFromJSON(const std::string& str, int& pos) { - return CValue(); + // skip opening bracket '[' cause it has already been checked + pos++; + // handle first element separately to get pattern: [(firstValue)(,value)(,value)...] + CValue firstValue = valueFromJSON(str, pos); + if (firstValue.IsUndefined()) + { + if (pos < str.size() && str[pos] == ']') + { + pos++; + return CValue::CreateArray(0); + } + else + { + return CValue(); + } + } + + std::vector values; + values.emplace_back(firstValue); + while (skipWhitespaces(str, pos) && str[pos] != ']') + { + // expect ',' + if (str[pos] != ',') + return CValue(); + pos++; + // expect value + CValue value = valueFromJSON(str, pos); + if (value.IsUndefined()) + return CValue(); + values.emplace_back(value); + } + // if did not encounter closing bracket ']', return undefined + if (pos == str.size()) + return CValue(); + pos++; + + // copy values from vector to CValue array + CValue result = CValue::CreateArray(values.size()); + for (int i = 0; i < values.size(); i++) + { + result[i] = values[i]; + } + + return result; } CValue parseObjectFromJSON(const std::string& str, int& pos) { return CValue(); } - } - CValue CValue::FromJSON(const std::string& jsonString) - { - CValue value; - int pos = 0; - while (skipWhitespaces(jsonString, pos)) + CValue valueFromJSON(const std::string& str, int& pos) { - // if value has been already parsed on previous iteration and there are still some symbols left, - // the json is considered invalid - if (!value.IsUndefined()) + if (!skipWhitespaces(str, pos)) return CValue(); - char ch = jsonString[pos]; + CValue value; + char ch = str[pos]; if (ch == '\"') { // string - value = parseStringFromJSON(jsonString, pos); + value = parseStringFromJSON(str, pos); } else if (ch == '[') { // array - value = parseArrayFromJSON(jsonString, pos); + value = parseArrayFromJSON(str, pos); } else if (ch == '{') { // object - value = parseObjectFromJSON(jsonString, pos); + value = parseObjectFromJSON(str, pos); } else if (ch == 'n') { // null - if (jsonString.substr(pos, 4) == "null") + if (str.substr(pos, 4) == "null") value = CValue::CreateNull(); else return CValue(); @@ -787,7 +827,7 @@ namespace NSJSON else if (ch == 't') { // true (bool) - if (jsonString.substr(pos, 4) == "true") + if (str.substr(pos, 4) == "true") value = true; else return CValue(); @@ -796,7 +836,7 @@ namespace NSJSON else if (ch == 'f') { // false (bool) - if (jsonString.substr(pos, 5) == "false") + if (str.substr(pos, 5) == "false") value = false; else return CValue(); @@ -804,16 +844,25 @@ namespace NSJSON } else if (ch == '-' || std::isdigit(ch)) { - value = parseNumberFromJSON(jsonString, pos); + value = parseNumberFromJSON(str, pos); } else { return CValue(); } - // if value is still undefined, the json is also invalid - if (value.IsUndefined()) - break; + + return value; } + } + + CValue CValue::FromJSON(const std::string& jsonString) + { + int pos = 0; + CValue value = valueFromJSON(jsonString, pos); + // if there are still some non-space characters after the value has been parsed, + // the JSON-string is considered invalid + if (skipWhitespaces(jsonString, pos)) + return CValue(); return value; } diff --git a/DesktopEditor/doctrenderer/test/json/main.cpp b/DesktopEditor/doctrenderer/test/json/main.cpp index 83ab41f857..d9ba6d9abc 100644 --- a/DesktopEditor/doctrenderer/test/json/main.cpp +++ b/DesktopEditor/doctrenderer/test/json/main.cpp @@ -1131,6 +1131,34 @@ TEST_F(CJSONTest, FromJSON_null) EXPECT_TRUE(val.IsNull()); } +TEST_F(CJSONTest, FromJSON_null_invalid) +{ + // empty json string is invalid + std::string strJson = ""; + CValue val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsUndefined()); + // there is no undefined in JSON standard + strJson = "undefined"; + val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsUndefined()); + + strJson = "nil"; + val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsUndefined()); + + strJson = "n"; + val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsUndefined()); + + strJson = "NULL"; + val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsUndefined()); + + strJson = "nullptr"; + val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsUndefined()); +} + TEST_F(CJSONTest, FromJSON_bool) { std::string strJson = "false"; @@ -1138,12 +1166,35 @@ TEST_F(CJSONTest, FromJSON_bool) EXPECT_TRUE(val.IsBool()); EXPECT_EQ((bool)val, false); - strJson = "true"; + strJson = "true "; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsBool()); EXPECT_EQ((bool)val, true); } +TEST_F(CJSONTest, FromJSON_bool_invalid) +{ + std::string strJson = "fals"; + CValue val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsUndefined()); + + strJson = "truE"; + val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsUndefined()); + + strJson = "f"; + val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsUndefined()); + + strJson = "t"; + val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsUndefined()); + + strJson = "True"; + val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsUndefined()); +} + TEST_F(CJSONTest, FromJSON_int) { std::string strJson = "42"; @@ -1164,17 +1215,18 @@ TEST_F(CJSONTest, FromJSON_int) strJson = "-2147483648"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsInt()); - EXPECT_EQ((int)val, -2147483648); + EXPECT_EQ((int)val, std::numeric_limits::min()); strJson = "2147483647"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsInt()); - EXPECT_EQ((int)val, 2147483647); + EXPECT_EQ((int)val, std::numeric_limits::max()); strJson = "2147483648"; val = CValue::FromJSON(strJson); // this value does not fit into 32-bit integer limits EXPECT_FALSE(val.IsInt()); + EXPECT_TRUE(val.IsDouble()); } TEST_F(CJSONTest, FromJSON_double) @@ -1443,7 +1495,158 @@ TEST_F(CJSONTest, FromJSON_whitespace_symbols) EXPECT_TRUE(val.IsUndefined()); } -// TODO: add incorrects JSONs +TEST_F(CJSONTest, FromJSON_arrays) +{ + std::string strJson = "[]"; + CValue val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsArray()); + EXPECT_EQ(val.GetCount(), 0); + EXPECT_TRUE(val[0].IsUndefined()); + + strJson = "[1, 2, 3]"; + val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsArray()); + EXPECT_EQ(val.GetCount(), 3); + EXPECT_EQ((int)val[0], 1); + EXPECT_EQ((int)val[1], 2); + EXPECT_EQ((int)val[2], 3); + + strJson = " [ \"][\" ] "; + val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsArray()); + EXPECT_EQ(val.GetCount(), 1); + EXPECT_EQ(val[0].ToStringA(), "]["); + + strJson = " [ 73 , null\n\t,4.2]"; + val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsArray()); + EXPECT_EQ(val.GetCount(), 3); + EXPECT_EQ((int)val[0], 73); + EXPECT_TRUE(val[1].IsNull()); + EXPECT_DOUBLE_EQ((double)val[2], 4.2); + + strJson = "[1, \"two\", 3.14, true, null]"; + val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsArray()); + EXPECT_EQ(val.GetCount(), 5); + EXPECT_EQ(val[0].ToInt(), 1); + EXPECT_EQ(val[1].ToStringA(), "two"); + EXPECT_DOUBLE_EQ(val[2].ToDouble(), 3.14); + EXPECT_TRUE(val[3].ToBool()); + EXPECT_TRUE(val[4].IsNull()); +} + +TEST_F(CJSONTest, FromJSON_arrays_inner) +{ + std::string strJson = "[[1, 2, 3]]"; + CValue val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsArray()); + EXPECT_EQ(val.GetCount(), 1); + EXPECT_TRUE(val[0].IsArray()); + EXPECT_EQ(val[0].GetCount(), 3); + EXPECT_EQ((int)val[0][0], 1); + EXPECT_EQ((int)val[0][1], 2); + EXPECT_EQ((int)val[0][2], 3); + + strJson = "[1, [2, 3], [4, [5, 6]], 7]"; + val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsArray()); + EXPECT_EQ(val.GetCount(), 4); + EXPECT_EQ((int)val[0], 1); + EXPECT_TRUE(val[1].IsArray()); + EXPECT_EQ(val[1].GetCount(), 2); + EXPECT_EQ((int)val[1][0], 2); + EXPECT_EQ((int)val[1][1], 3); + EXPECT_TRUE(val[2].IsArray()); + EXPECT_EQ(val[2].GetCount(), 2); + EXPECT_EQ((int)val[2][0], 4); + EXPECT_TRUE(val[2][1].IsArray()); + EXPECT_EQ(val[2][1].GetCount(), 2); + EXPECT_EQ((int)val[2][1][0], 5); + EXPECT_EQ((int)val[2][1][1], 6); + EXPECT_EQ((int)val[3], 7); + + strJson = "[[],[[]],[[],[]]]"; + val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsArray()); + EXPECT_EQ(val.GetCount(), 3); + EXPECT_TRUE(val[0].IsArray()); + EXPECT_EQ(val[0].GetCount(), 0); + EXPECT_TRUE(val[1].IsArray()); + EXPECT_EQ(val[1].GetCount(), 1); + EXPECT_TRUE(val[1][0].IsArray()); + EXPECT_EQ(val[1][0].GetCount(), 0); + EXPECT_TRUE(val[2].IsArray()); + EXPECT_EQ(val[2].GetCount(), 2); + EXPECT_TRUE(val[2][0].IsArray()); + EXPECT_EQ(val[2][0].GetCount(), 0); + EXPECT_TRUE(val[2][1].IsArray()); + EXPECT_EQ(val[2][1].GetCount(), 0); +} + +TEST_F(CJSONTest, FromJSON_arrays_invalid) +{ + // no closing bracket + std::string strJson = "["; + CValue val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsUndefined()); + strJson = "[1,2,3"; + val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsUndefined()); + // trailing comma + strJson = "[1,2,]"; + val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsUndefined()); + // leading comma + strJson = "[,1]"; + val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsUndefined()); + // missing comma + strJson = "[1 2, 3]"; + val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsUndefined()); + // extra commas + strJson = "[1,,2,3]"; + val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsUndefined()); + strJson = "[,]"; + val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsUndefined()); + // invalid elements + strJson = "[1, 2, undefined, 4]"; + val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsUndefined()); + // non-JSON values as elements + strJson = "[1, 2, f(), 4]"; + val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsUndefined()); + // invalid syntax within elements + strJson = "[\"foo\", 123, [1, 2, }, 4]"; + val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsUndefined()); + // whitespace issues + strJson = "[1, 2 3, 4]"; + val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsUndefined()); + // empty array without brackets + strJson = "]"; + val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsUndefined()); + // extra brackets + strJson = "[[1, 2, 3]"; + val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsUndefined()); + strJson = "[1, 2, 3]]"; + val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsUndefined()); + // elements outside of the array + strJson = "[1, 2, 3], 5"; + val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsUndefined()); + strJson = "[1, 2, 3] 4"; + val = CValue::FromJSON(strJson); + EXPECT_TRUE(val.IsUndefined()); +} #else int main()