Skip to content

Commit d9b1e7e

Browse files
committed
Allowed non-quoted key to contain underscores (fixes bblanchon#665)
1 parent b4eece0 commit d9b1e7e

File tree

4 files changed

+161
-145
lines changed

4 files changed

+161
-145
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ HEAD
55
----
66

77
* Fixed `JsonVariant::operator|(int)` that returned the default value if the variant contained a double (issue #675)
8+
* Allowed non-quoted key to contain underscores (issue #665)
89

910
v5.13.0
1011
-------

src/ArduinoJson/Deserialization/JsonParser.hpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,13 @@ class JsonParser {
5050
inline bool parseObjectTo(JsonVariant *destination);
5151
inline bool parseStringTo(JsonVariant *destination);
5252

53-
static inline bool isInRange(char c, char min, char max) {
53+
static inline bool isBetween(char c, char min, char max) {
5454
return min <= c && c <= max;
5555
}
5656

57-
static inline bool isLetterOrNumber(char c) {
58-
return isInRange(c, '0', '9') || isInRange(c, 'a', 'z') ||
59-
isInRange(c, 'A', 'Z') || c == '+' || c == '-' || c == '.';
57+
static inline bool canBeInNonQuotedString(char c) {
58+
return isBetween(c, '0', '9') || isBetween(c, '_', 'z') ||
59+
isBetween(c, 'A', 'Z') || c == '+' || c == '-' || c == '.';
6060
}
6161

6262
static inline bool isQuote(char c) {
@@ -99,5 +99,5 @@ inline typename JsonParserBuilder<TJsonBuffer, TString>::TParser makeParser(
9999
return JsonParserBuilder<TJsonBuffer, TString>::makeParser(buffer, json,
100100
nestingLimit);
101101
}
102-
}
103-
}
102+
} // namespace Internals
103+
} // namespace ArduinoJson

src/ArduinoJson/Deserialization/JsonParserImpl.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseString() {
167167
}
168168
} else { // no quotes
169169
for (;;) {
170-
if (!isLetterOrNumber(c)) break;
170+
if (!canBeInNonQuotedString(c)) break;
171171
_reader.move();
172172
str.append(c);
173173
c = _reader.current();

test/JsonBuffer/parseObject.cpp

Lines changed: 153 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -8,148 +8,163 @@
88
TEST_CASE("JsonBuffer::parseObject()") {
99
DynamicJsonBuffer jb;
1010

11-
SECTION("EmptyObject") {
11+
SECTION("An empty object") {
1212
JsonObject& obj = jb.parseObject("{}");
1313
REQUIRE(obj.success());
1414
REQUIRE(obj.size() == 0);
1515
}
1616

17-
SECTION("MissingOpeningBrace") {
18-
JsonObject& obj = jb.parseObject("}");
19-
REQUIRE_FALSE(obj.success());
20-
}
21-
22-
SECTION("MissingClosingBrace") {
23-
JsonObject& obj = jb.parseObject("{");
24-
REQUIRE_FALSE(obj.success());
25-
}
26-
27-
SECTION("MissingColonAndValue") {
28-
JsonObject& obj = jb.parseObject("{\"key\"}");
29-
REQUIRE_FALSE(obj.success());
30-
}
31-
32-
SECTION("MissingQuotesAndColonAndValue") {
33-
JsonObject& obj = jb.parseObject("{key}");
34-
REQUIRE_FALSE(obj.success());
35-
}
36-
37-
SECTION("OneString") {
38-
JsonObject& obj = jb.parseObject("{\"key\":\"value\"}");
39-
REQUIRE(obj.success());
40-
REQUIRE(obj.size() == 1);
41-
REQUIRE(obj["key"] == "value");
42-
}
43-
44-
SECTION("OneStringSingleQuotes") {
45-
JsonObject& obj = jb.parseObject("{'key':'value'}");
46-
REQUIRE(obj.success());
47-
REQUIRE(obj.size() == 1);
48-
REQUIRE(obj["key"] == "value");
49-
}
50-
51-
SECTION("OneStringNoQuotes") {
52-
JsonObject& obj = jb.parseObject("{key:value}");
53-
REQUIRE(obj.success());
54-
REQUIRE(obj.size() == 1);
55-
REQUIRE(obj["key"] == "value");
56-
}
57-
58-
SECTION("OneStringSpaceBeforeKey") {
59-
JsonObject& obj = jb.parseObject("{ \"key\":\"value\"}");
60-
REQUIRE(obj.success());
61-
REQUIRE(obj.size() == 1);
62-
REQUIRE(obj["key"] == "value");
63-
}
64-
65-
SECTION("OneStringSpaceAfterKey") {
66-
JsonObject& obj = jb.parseObject("{\"key\" :\"value\"}");
67-
REQUIRE(obj.success());
68-
REQUIRE(obj.size() == 1);
69-
REQUIRE(obj["key"] == "value");
70-
}
71-
72-
SECTION("OneStringSpaceBeforeValue") {
73-
JsonObject& obj = jb.parseObject("{\"key\": \"value\"}");
74-
REQUIRE(obj.success());
75-
REQUIRE(obj.size() == 1);
76-
REQUIRE(obj["key"] == "value");
77-
}
78-
79-
SECTION("OneStringSpaceAfterValue") {
80-
JsonObject& obj = jb.parseObject("{\"key\":\"value\" }");
81-
REQUIRE(obj.success());
82-
REQUIRE(obj.size() == 1);
83-
REQUIRE(obj["key"] == "value");
84-
}
85-
86-
SECTION("TwoStrings") {
87-
JsonObject& obj =
88-
jb.parseObject("{\"key1\":\"value1\",\"key2\":\"value2\"}");
89-
REQUIRE(obj.success());
90-
REQUIRE(obj.size() == 2);
91-
REQUIRE(obj["key1"] == "value1");
92-
REQUIRE(obj["key2"] == "value2");
93-
}
94-
95-
SECTION("TwoStringsSpaceBeforeComma") {
96-
JsonObject& obj =
97-
jb.parseObject("{\"key1\":\"value1\" ,\"key2\":\"value2\"}");
98-
REQUIRE(obj.success());
99-
REQUIRE(obj.size() == 2);
100-
REQUIRE(obj["key1"] == "value1");
101-
REQUIRE(obj["key2"] == "value2");
102-
}
103-
104-
SECTION("TwoStringsSpaceAfterComma") {
105-
JsonObject& obj =
106-
jb.parseObject("{\"key1\":\"value1\" ,\"key2\":\"value2\"}");
107-
REQUIRE(obj.success());
108-
REQUIRE(obj.size() == 2);
109-
REQUIRE(obj["key1"] == "value1");
110-
REQUIRE(obj["key2"] == "value2");
111-
}
112-
113-
SECTION("EndingWithAComma") {
114-
JsonObject& obj = jb.parseObject("{\"key1\":\"value1\",}");
115-
REQUIRE_FALSE(obj.success());
116-
REQUIRE(obj.size() == 0);
117-
}
118-
119-
SECTION("TwoIntergers") {
120-
JsonObject& obj = jb.parseObject("{\"key1\":42,\"key2\":-42}");
121-
REQUIRE(obj.success());
122-
REQUIRE(obj.size() == 2);
123-
REQUIRE(obj["key1"] == 42);
124-
REQUIRE(obj["key2"] == -42);
125-
}
126-
127-
SECTION("TwoDoubles") {
128-
JsonObject& obj = jb.parseObject("{\"key1\":12.345,\"key2\":-7E89}");
129-
REQUIRE(obj.success());
130-
REQUIRE(obj.size() == 2);
131-
REQUIRE(obj["key1"] == 12.345);
132-
REQUIRE(obj["key2"] == -7E89);
133-
}
134-
135-
SECTION("TwoBooleans") {
136-
JsonObject& obj = jb.parseObject("{\"key1\":true,\"key2\":false}");
137-
REQUIRE(obj.success());
138-
REQUIRE(obj.size() == 2);
139-
REQUIRE(obj["key1"] == true);
140-
REQUIRE(obj["key2"] == false);
141-
}
142-
143-
SECTION("TwoNulls") {
144-
JsonObject& obj = jb.parseObject("{\"key1\":null,\"key2\":null}");
145-
REQUIRE(obj.success());
146-
REQUIRE(obj.size() == 2);
147-
REQUIRE(obj["key1"].as<char*>() == 0);
148-
REQUIRE(obj["key2"].as<char*>() == 0);
149-
}
150-
151-
SECTION("NullForKey") {
152-
JsonObject& obj = jb.parseObject("null:\"value\"}");
153-
REQUIRE_FALSE(obj.success());
17+
SECTION("Quotes") {
18+
SECTION("Double quotes") {
19+
JsonObject& obj = jb.parseObject("{\"key\":\"value\"}");
20+
REQUIRE(obj.success());
21+
REQUIRE(obj.size() == 1);
22+
REQUIRE(obj["key"] == "value");
23+
}
24+
25+
SECTION("Single quotes") {
26+
JsonObject& obj = jb.parseObject("{'key':'value'}");
27+
REQUIRE(obj.success());
28+
REQUIRE(obj.size() == 1);
29+
REQUIRE(obj["key"] == "value");
30+
}
31+
32+
SECTION("No quotes") {
33+
JsonObject& obj = jb.parseObject("{key:value}");
34+
REQUIRE(obj.success());
35+
REQUIRE(obj.size() == 1);
36+
REQUIRE(obj["key"] == "value");
37+
}
38+
39+
SECTION("No quotes, allow underscore in key") {
40+
JsonObject& obj = jb.parseObject("{_k_e_y_:42}");
41+
REQUIRE(obj.success());
42+
REQUIRE(obj.size() == 1);
43+
REQUIRE(obj["_k_e_y_"] == 42);
44+
}
45+
}
46+
47+
SECTION("Spaces") {
48+
SECTION("Before the key") {
49+
JsonObject& obj = jb.parseObject("{ \"key\":\"value\"}");
50+
REQUIRE(obj.success());
51+
REQUIRE(obj.size() == 1);
52+
REQUIRE(obj["key"] == "value");
53+
}
54+
55+
SECTION("After the key") {
56+
JsonObject& obj = jb.parseObject("{\"key\" :\"value\"}");
57+
REQUIRE(obj.success());
58+
REQUIRE(obj.size() == 1);
59+
REQUIRE(obj["key"] == "value");
60+
}
61+
62+
SECTION("Before the value") {
63+
JsonObject& obj = jb.parseObject("{\"key\": \"value\"}");
64+
REQUIRE(obj.success());
65+
REQUIRE(obj.size() == 1);
66+
REQUIRE(obj["key"] == "value");
67+
}
68+
69+
SECTION("After the value") {
70+
JsonObject& obj = jb.parseObject("{\"key\":\"value\" }");
71+
REQUIRE(obj.success());
72+
REQUIRE(obj.size() == 1);
73+
REQUIRE(obj["key"] == "value");
74+
}
75+
76+
SECTION("Before the colon") {
77+
JsonObject& obj =
78+
jb.parseObject("{\"key1\":\"value1\" ,\"key2\":\"value2\"}");
79+
REQUIRE(obj.success());
80+
REQUIRE(obj.size() == 2);
81+
REQUIRE(obj["key1"] == "value1");
82+
REQUIRE(obj["key2"] == "value2");
83+
}
84+
85+
SECTION("After the colon") {
86+
JsonObject& obj =
87+
jb.parseObject("{\"key1\":\"value1\" ,\"key2\":\"value2\"}");
88+
REQUIRE(obj.success());
89+
REQUIRE(obj.size() == 2);
90+
REQUIRE(obj["key1"] == "value1");
91+
REQUIRE(obj["key2"] == "value2");
92+
}
93+
}
94+
95+
SECTION("Values types") {
96+
SECTION("String") {
97+
JsonObject& obj =
98+
jb.parseObject("{\"key1\":\"value1\",\"key2\":\"value2\"}");
99+
REQUIRE(obj.success());
100+
REQUIRE(obj.size() == 2);
101+
REQUIRE(obj["key1"] == "value1");
102+
REQUIRE(obj["key2"] == "value2");
103+
}
104+
105+
SECTION("Integer") {
106+
JsonObject& obj = jb.parseObject("{\"key1\":42,\"key2\":-42}");
107+
REQUIRE(obj.success());
108+
REQUIRE(obj.size() == 2);
109+
REQUIRE(obj["key1"] == 42);
110+
REQUIRE(obj["key2"] == -42);
111+
}
112+
113+
SECTION("Double") {
114+
JsonObject& obj = jb.parseObject("{\"key1\":12.345,\"key2\":-7E89}");
115+
REQUIRE(obj.success());
116+
REQUIRE(obj.size() == 2);
117+
REQUIRE(obj["key1"] == 12.345);
118+
REQUIRE(obj["key2"] == -7E89);
119+
}
120+
121+
SECTION("Booleans") {
122+
JsonObject& obj = jb.parseObject("{\"key1\":true,\"key2\":false}");
123+
REQUIRE(obj.success());
124+
REQUIRE(obj.size() == 2);
125+
REQUIRE(obj["key1"] == true);
126+
REQUIRE(obj["key2"] == false);
127+
}
128+
129+
SECTION("Null") {
130+
JsonObject& obj = jb.parseObject("{\"key1\":null,\"key2\":null}");
131+
REQUIRE(obj.success());
132+
REQUIRE(obj.size() == 2);
133+
REQUIRE(obj["key1"].as<char*>() == 0);
134+
REQUIRE(obj["key2"].as<char*>() == 0);
135+
}
136+
}
137+
138+
SECTION("Misc") {
139+
SECTION("The opening brace is missing") {
140+
JsonObject& obj = jb.parseObject("}");
141+
REQUIRE_FALSE(obj.success());
142+
}
143+
144+
SECTION("The closing brace is missing") {
145+
JsonObject& obj = jb.parseObject("{");
146+
REQUIRE_FALSE(obj.success());
147+
}
148+
149+
SECTION("A quoted key without value") {
150+
JsonObject& obj = jb.parseObject("{\"key\"}");
151+
REQUIRE_FALSE(obj.success());
152+
}
153+
154+
SECTION("A non-quoted key without value") {
155+
JsonObject& obj = jb.parseObject("{key}");
156+
REQUIRE_FALSE(obj.success());
157+
}
158+
159+
SECTION("A dangling comma") {
160+
JsonObject& obj = jb.parseObject("{\"key1\":\"value1\",}");
161+
REQUIRE_FALSE(obj.success());
162+
REQUIRE(obj.size() == 0);
163+
}
164+
165+
SECTION("null as a key") {
166+
JsonObject& obj = jb.parseObject("null:\"value\"}");
167+
REQUIRE_FALSE(obj.success());
168+
}
154169
}
155170
}

0 commit comments

Comments
 (0)