Skip to content

Commit a1b6c2d

Browse files
committed
Merge branch 'parse-escaped-chars'
2 parents 7a3fa35 + feb6060 commit a1b6c2d

File tree

9 files changed

+195
-47
lines changed

9 files changed

+195
-47
lines changed

CHANGELOG.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
Arduino JSON: change log
22
========================
33

4+
v3.4
5+
----
6+
7+
* Fixed escaped char parsing (issue #16)
8+
9+
410
v3.3
511
----
612

7-
* Added indented output for the JSON generator, see example bellow.
13+
* Added indented output for the JSON generator (issue #11), see example bellow.
814
* Added `IndentedPrint`, a decorator for `Print` to allow indented output
915

1016
Example:
@@ -23,7 +29,7 @@ v3.1
2329

2430
* Calling `Generator::JsonObject::add()` twice with the same `key` now replaces the `value`
2531
* Added `Generator::JsonObject::operator[]`, see bellow the new API
26-
* Added `Generator::JsonObject::remove()`
32+
* Added `Generator::JsonObject::remove()` (issue #9)
2733

2834
Old generator API:
2935

@@ -44,7 +50,7 @@ v3.0
4450

4551
* New parser API, see bellow
4652
* Renamed `JsonHashTable` into `JsonObject`
47-
* Added iterators for `JsonArray` and `JsonObject`
53+
* Added iterators for `JsonArray` and `JsonObject` (issue #4)
4854

4955
Old parser API:
5056

JsonParser/JsonToken.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,50 @@
77

88
using namespace ArduinoJson::Parser;
99

10+
char* JsonToken::getText()
11+
{
12+
char* s = json + token->start;
13+
json[token->end] = 0;
14+
15+
unescapeString(s);
16+
17+
return s;
18+
}
19+
20+
inline void JsonToken::unescapeString(char* s)
21+
{
22+
char* readPtr = s;
23+
char* writePtr = s;
24+
char c;
25+
26+
do
27+
{
28+
c = *readPtr++;
29+
30+
if (c == '\\')
31+
{
32+
c = unescapeChar(*readPtr++);
33+
}
34+
35+
*writePtr++ = c;
36+
37+
} while (c != 0);
38+
}
39+
40+
inline char JsonToken::unescapeChar(char c)
41+
{
42+
// Optimized for code size on a 8-bit AVR
43+
44+
const char* p = "b\bf\fn\nr\rt\t";
45+
46+
while (true)
47+
{
48+
if (p[0] == 0) return c;
49+
if (p[0] == c) return p[1];
50+
p += 2;
51+
}
52+
}
53+
1054
JsonToken JsonToken::nextSibling() const
1155
{
1256
// start with current token

JsonParser/JsonToken.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,7 @@ namespace ArduinoJson
2929
}
3030

3131
// Get content of the JSON token
32-
char* getText()
33-
{
34-
json[token->end] = 0;
35-
return json + token->start;
36-
}
32+
char* getText();
3733

3834
// Get the number of children tokens
3935
int childrenCount()
@@ -95,6 +91,9 @@ namespace ArduinoJson
9591
private:
9692
char* json;
9793
jsmntok_t* token;
94+
95+
static char unescapeChar(char c);
96+
static void unescapeString(char* s);
9897
};
9998
}
10099
}

JsonParserTests/JsonArrayTests.cpp

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,33 +11,27 @@ using namespace ArduinoJson::Parser;
1111

1212
namespace ArduinoJsonParserTests
1313
{
14-
TEST_CLASS(JsonArrayTests)
15-
{
14+
TEST_CLASS(JsonArrayTests)
15+
{
1616
JsonArray array;
1717
char json[256];
1818
jsmntok_t tokens[32];
1919
JsonParserBase parser = JsonParserBase(tokens, 32);
2020

21-
public:
22-
23-
TEST_METHOD(EmptyString)
24-
{
25-
whenInputIs("");
26-
parseMustFail();
27-
}
28-
29-
TEST_METHOD(TooFewClosingBrackets)
30-
{
21+
public:
22+
23+
TEST_METHOD(TooFewClosingBrackets)
24+
{
3125
whenInputIs("[[]");
3226
parseMustFail();
33-
}
27+
}
3428

35-
TEST_METHOD(TooManyClosingBrackets)
36-
{
29+
TEST_METHOD(TooManyClosingBrackets)
30+
{
3731
whenInputIs("[]]");
3832
parseMustFail();
39-
}
40-
33+
}
34+
4135
TEST_METHOD(EmptyArray)
4236
{
4337
whenInputIs("[]");
@@ -55,8 +49,8 @@ namespace ArduinoJsonParserTests
5549
itemMustNotExist(0);
5650
}
5751

58-
TEST_METHOD(TwoIntegers)
59-
{
52+
TEST_METHOD(TwoIntegers)
53+
{
6054
setTokenCountTo(3);
6155

6256
whenInputIs("[1,2]");
@@ -66,7 +60,7 @@ namespace ArduinoJsonParserTests
6660
itemMustBe(0, 1L);
6761
itemMustBe(1, 2L);
6862
itemMustNotExist(2);
69-
}
63+
}
7064

7165
TEST_METHOD(TwoBooleans)
7266
{
@@ -94,8 +88,8 @@ namespace ArduinoJsonParserTests
9488
itemMustNotExist(2);
9589
}
9690

97-
TEST_METHOD(TwoDimensionsArray)
98-
{
91+
TEST_METHOD(TwoDimensionsArray)
92+
{
9993
setTokenCountTo(7);
10094

10195
whenInputIs("[[1,2],[3,4]]");
@@ -107,7 +101,7 @@ namespace ArduinoJsonParserTests
107101
itemMustBe(1, 0, 3L);
108102
itemMustBe(1, 1, 4L);
109103
itemMustNotExist(2);
110-
}
104+
}
111105

112106
TEST_METHOD(ThreeDimensionsArray)
113107
{
@@ -127,7 +121,7 @@ namespace ArduinoJsonParserTests
127121
itemMustBe(1, 1, 1, 8L);
128122
itemMustNotExist(2);
129123
}
130-
124+
131125
private:
132126

133127
void setTokenCountTo(int n)
@@ -191,5 +185,5 @@ namespace ArduinoJsonParserTests
191185
Assert::AreEqual(0L, array.getLong(index));
192186
Assert::IsNull(array.getString(index));
193187
}
194-
};
188+
};
195189
}

JsonParserTests/JsonObjectTests.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,6 @@ namespace ArduinoJsonParserTests
2323

2424
public:
2525

26-
TEST_METHOD(EmptyString)
27-
{
28-
whenInputIs("");
29-
parseMustFail();
30-
}
31-
3226
TEST_METHOD(EmptyHashTable)
3327
{
3428
whenInputIs("{}");

JsonParserTests/JsonParserTests.vcxproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
<ClCompile Include="JsonArrayIteratorTests.cpp" />
9191
<ClCompile Include="JsonObjectTests.cpp" />
9292
<ClCompile Include="GbathreeBug.cpp" />
93+
<ClCompile Include="JsonStringTests.cpp" />
9394
</ItemGroup>
9495
<ItemGroup>
9596
<ProjectReference Include="..\JsonParser\JsonParser.vcxproj">

JsonParserTests/JsonParserTests.vcxproj.filters

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@
1515
</Filter>
1616
</ItemGroup>
1717
<ItemGroup>
18-
<ClCompile Include="JsonArrayTests.cpp">
19-
<Filter>Source Files</Filter>
20-
</ClCompile>
2118
<ClCompile Include="GbathreeBug.cpp">
2219
<Filter>Source Files</Filter>
2320
</ClCompile>
@@ -30,5 +27,11 @@
3027
<ClCompile Include="JsonObjectIteratorTests.cpp">
3128
<Filter>Source Files</Filter>
3229
</ClCompile>
30+
<ClCompile Include="JsonArrayTests.cpp">
31+
<Filter>Source Files</Filter>
32+
</ClCompile>
33+
<ClCompile Include="JsonStringTests.cpp">
34+
<Filter>Source Files</Filter>
35+
</ClCompile>
3336
</ItemGroup>
3437
</Project>

JsonParserTests/JsonStringTests.cpp

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
* Arduino JSON library
3+
* Benoit Blanchon 2014 - MIT License
4+
*/
5+
6+
#include "CppUnitTest.h"
7+
#include "JsonParser.h"
8+
9+
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
10+
using namespace ArduinoJson::Parser;
11+
12+
namespace ArduinoJsonParserTests
13+
{
14+
TEST_CLASS(JsonStringTests)
15+
{
16+
const char* actual;
17+
char json[256];
18+
JsonParser<32> parser;
19+
20+
public:
21+
22+
TEST_METHOD(EmptyString)
23+
{
24+
whenInputIs("");
25+
outputMustBe(0);
26+
}
27+
28+
TEST_METHOD(JustOneQuote)
29+
{
30+
whenInputIs("\"");
31+
outputMustBe(0);
32+
}
33+
34+
TEST_METHOD(SimpleString)
35+
{
36+
whenInputIs("\"Hi!\"");
37+
outputMustBe("Hi!");
38+
}
39+
40+
TEST_METHOD(EscapedQuote)
41+
{
42+
whenInputIs("\"12\\\"34\""); // ie 12\"34
43+
outputMustBe("12\"34");
44+
}
45+
46+
TEST_METHOD(EscapedReverseSolidus)
47+
{
48+
whenInputIs("\"12\\\\34\""); // ie 12\\34
49+
outputMustBe("12\\34");
50+
}
51+
52+
TEST_METHOD(EscapedSolidus)
53+
{
54+
whenInputIs("\"12\\/34\"");
55+
outputMustBe("12/34");
56+
}
57+
58+
TEST_METHOD(EscapedBackspace)
59+
{
60+
whenInputIs("\"12\\b34\"");
61+
outputMustBe("12\b34");
62+
}
63+
64+
TEST_METHOD(EscapedFormfeed)
65+
{
66+
whenInputIs("\"12\\f34\"");
67+
outputMustBe("12\f34");
68+
}
69+
70+
TEST_METHOD(EscapedNewline)
71+
{
72+
whenInputIs("\"12\\n34\"");
73+
outputMustBe("12\n34");
74+
}
75+
76+
TEST_METHOD(EscapedCarriageReturn)
77+
{
78+
whenInputIs("\"12\\r34\"");
79+
outputMustBe("12\r34");
80+
}
81+
82+
TEST_METHOD(EscapedTab)
83+
{
84+
whenInputIs("\"12\\t34\"");
85+
outputMustBe("12\t34");
86+
}
87+
88+
TEST_METHOD(AllEscapedCharsTogether)
89+
{
90+
whenInputIs("\"1\\\"2\\\\3\\/4\\b5\\f6\\n7\\r8\\t9\"");
91+
outputMustBe("1\"2\\3/4\b5\f6\n7\r8\t9");
92+
}
93+
94+
private:
95+
96+
void whenInputIs(const char* input)
97+
{
98+
strcpy(json, input);
99+
actual = parser.parse(json);
100+
}
101+
102+
void outputMustBe(const char* expected)
103+
{
104+
Assert::AreEqual(expected, actual);
105+
}
106+
};
107+
}

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ It has been written with Arduino in mind, but it isn't linked to Arduino librari
1010
Features
1111
--------
1212

13-
* JSON decoding: [more details here](/JsonParser/)
14-
* JSON encoding: [more details here](/JsonGenerator/)
13+
* JSON decoding: [see documentation here](/JsonParser/)
14+
* JSON encoding: [see documentation here](/JsonGenerator/)
1515
* Elegant API, very easy to use
1616
* Fixed memory allocation (no malloc)
1717
* Small footprint
@@ -22,9 +22,9 @@ Feature comparison
2222

2323
| Library | Memory allocation | Nested objects | Parser size | Encoder size |
2424
| ------------ | ----------------- | -------------- | ----------- | ------------- |
25-
| Arduino JSON | static | yes | 2642 Bytes | 862 bytes |
26-
| json-arduino | dynamic | no | 3348 (+27%) | not supported |
27-
| aJson | dynamic | yes | 5088 (+93%) | 4678 (+540%) |
25+
| Arduino JSON | static | yes | 2760 Bytes | 862 bytes |
26+
| json-arduino | dynamic | no | 3348 (+21%) | not supported |
27+
| aJson | dynamic | yes | 5088 (+84%) | 4678 (+540%) |
2828

2929
"Parser size" was measured with a program parsing `{"sensor":"outdoor","value":25.6}`.
3030
For each library, I wrote a program that extracts a string and a float. I subtracted the size of a program doing the same without any JSON parsing involved. [Source files are here](https://gist.github.com/bblanchon/e8ba914a7109f3642c0f).

0 commit comments

Comments
 (0)