Skip to content

Commit

Permalink
Merge branch 'parse-escaped-chars'
Browse files Browse the repository at this point in the history
  • Loading branch information
bblanchon committed Sep 9, 2014
2 parents 7a3fa35 + feb6060 commit a1b6c2d
Show file tree
Hide file tree
Showing 9 changed files with 195 additions and 47 deletions.
12 changes: 9 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
Arduino JSON: change log
========================

v3.4
----

* Fixed escaped char parsing (issue #16)


v3.3
----

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

Example:
Expand All @@ -23,7 +29,7 @@ v3.1

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

Old generator API:

Expand All @@ -44,7 +50,7 @@ v3.0

* New parser API, see bellow
* Renamed `JsonHashTable` into `JsonObject`
* Added iterators for `JsonArray` and `JsonObject`
* Added iterators for `JsonArray` and `JsonObject` (issue #4)

Old parser API:

Expand Down
44 changes: 44 additions & 0 deletions JsonParser/JsonToken.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,50 @@

using namespace ArduinoJson::Parser;

char* JsonToken::getText()
{
char* s = json + token->start;
json[token->end] = 0;

unescapeString(s);

return s;
}

inline void JsonToken::unescapeString(char* s)
{
char* readPtr = s;
char* writePtr = s;
char c;

do
{
c = *readPtr++;

if (c == '\\')
{
c = unescapeChar(*readPtr++);
}

*writePtr++ = c;

} while (c != 0);
}

inline char JsonToken::unescapeChar(char c)
{
// Optimized for code size on a 8-bit AVR

const char* p = "b\bf\fn\nr\rt\t";

while (true)
{
if (p[0] == 0) return c;
if (p[0] == c) return p[1];
p += 2;
}
}

JsonToken JsonToken::nextSibling() const
{
// start with current token
Expand Down
9 changes: 4 additions & 5 deletions JsonParser/JsonToken.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,7 @@ namespace ArduinoJson
}

// Get content of the JSON token
char* getText()
{
json[token->end] = 0;
return json + token->start;
}
char* getText();

// Get the number of children tokens
int childrenCount()
Expand Down Expand Up @@ -95,6 +91,9 @@ namespace ArduinoJson
private:
char* json;
jsmntok_t* token;

static char unescapeChar(char c);
static void unescapeString(char* s);
};
}
}
44 changes: 19 additions & 25 deletions JsonParserTests/JsonArrayTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,27 @@ using namespace ArduinoJson::Parser;

namespace ArduinoJsonParserTests
{
TEST_CLASS(JsonArrayTests)
{
TEST_CLASS(JsonArrayTests)
{
JsonArray array;
char json[256];
jsmntok_t tokens[32];
JsonParserBase parser = JsonParserBase(tokens, 32);

public:

TEST_METHOD(EmptyString)
{
whenInputIs("");
parseMustFail();
}

TEST_METHOD(TooFewClosingBrackets)
{
public:

TEST_METHOD(TooFewClosingBrackets)
{
whenInputIs("[[]");
parseMustFail();
}
}

TEST_METHOD(TooManyClosingBrackets)
{
TEST_METHOD(TooManyClosingBrackets)
{
whenInputIs("[]]");
parseMustFail();
}
}
TEST_METHOD(EmptyArray)
{
whenInputIs("[]");
Expand All @@ -55,8 +49,8 @@ namespace ArduinoJsonParserTests
itemMustNotExist(0);
}

TEST_METHOD(TwoIntegers)
{
TEST_METHOD(TwoIntegers)
{
setTokenCountTo(3);

whenInputIs("[1,2]");
Expand All @@ -66,7 +60,7 @@ namespace ArduinoJsonParserTests
itemMustBe(0, 1L);
itemMustBe(1, 2L);
itemMustNotExist(2);
}
}

TEST_METHOD(TwoBooleans)
{
Expand Down Expand Up @@ -94,8 +88,8 @@ namespace ArduinoJsonParserTests
itemMustNotExist(2);
}

TEST_METHOD(TwoDimensionsArray)
{
TEST_METHOD(TwoDimensionsArray)
{
setTokenCountTo(7);

whenInputIs("[[1,2],[3,4]]");
Expand All @@ -107,7 +101,7 @@ namespace ArduinoJsonParserTests
itemMustBe(1, 0, 3L);
itemMustBe(1, 1, 4L);
itemMustNotExist(2);
}
}

TEST_METHOD(ThreeDimensionsArray)
{
Expand All @@ -127,7 +121,7 @@ namespace ArduinoJsonParserTests
itemMustBe(1, 1, 1, 8L);
itemMustNotExist(2);
}
private:

void setTokenCountTo(int n)
Expand Down Expand Up @@ -191,5 +185,5 @@ namespace ArduinoJsonParserTests
Assert::AreEqual(0L, array.getLong(index));
Assert::IsNull(array.getString(index));
}
};
};
}
6 changes: 0 additions & 6 deletions JsonParserTests/JsonObjectTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,6 @@ namespace ArduinoJsonParserTests

public:

TEST_METHOD(EmptyString)
{
whenInputIs("");
parseMustFail();
}

TEST_METHOD(EmptyHashTable)
{
whenInputIs("{}");
Expand Down
1 change: 1 addition & 0 deletions JsonParserTests/JsonParserTests.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
<ClCompile Include="JsonArrayIteratorTests.cpp" />
<ClCompile Include="JsonObjectTests.cpp" />
<ClCompile Include="GbathreeBug.cpp" />
<ClCompile Include="JsonStringTests.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\JsonParser\JsonParser.vcxproj">
Expand Down
9 changes: 6 additions & 3 deletions JsonParserTests/JsonParserTests.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="JsonArrayTests.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="GbathreeBug.cpp">
<Filter>Source Files</Filter>
</ClCompile>
Expand All @@ -30,5 +27,11 @@
<ClCompile Include="JsonObjectIteratorTests.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="JsonArrayTests.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="JsonStringTests.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>
107 changes: 107 additions & 0 deletions JsonParserTests/JsonStringTests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Arduino JSON library
* Benoit Blanchon 2014 - MIT License
*/

#include "CppUnitTest.h"
#include "JsonParser.h"

using namespace Microsoft::VisualStudio::CppUnitTestFramework;
using namespace ArduinoJson::Parser;

namespace ArduinoJsonParserTests
{
TEST_CLASS(JsonStringTests)
{
const char* actual;
char json[256];
JsonParser<32> parser;

public:

TEST_METHOD(EmptyString)
{
whenInputIs("");
outputMustBe(0);
}

TEST_METHOD(JustOneQuote)
{
whenInputIs("\"");
outputMustBe(0);
}

TEST_METHOD(SimpleString)
{
whenInputIs("\"Hi!\"");
outputMustBe("Hi!");
}

TEST_METHOD(EscapedQuote)
{
whenInputIs("\"12\\\"34\""); // ie 12\"34
outputMustBe("12\"34");
}

TEST_METHOD(EscapedReverseSolidus)
{
whenInputIs("\"12\\\\34\""); // ie 12\\34
outputMustBe("12\\34");
}

TEST_METHOD(EscapedSolidus)
{
whenInputIs("\"12\\/34\"");
outputMustBe("12/34");
}

TEST_METHOD(EscapedBackspace)
{
whenInputIs("\"12\\b34\"");
outputMustBe("12\b34");
}

TEST_METHOD(EscapedFormfeed)
{
whenInputIs("\"12\\f34\"");
outputMustBe("12\f34");
}

TEST_METHOD(EscapedNewline)
{
whenInputIs("\"12\\n34\"");
outputMustBe("12\n34");
}

TEST_METHOD(EscapedCarriageReturn)
{
whenInputIs("\"12\\r34\"");
outputMustBe("12\r34");
}

TEST_METHOD(EscapedTab)
{
whenInputIs("\"12\\t34\"");
outputMustBe("12\t34");
}

TEST_METHOD(AllEscapedCharsTogether)
{
whenInputIs("\"1\\\"2\\\\3\\/4\\b5\\f6\\n7\\r8\\t9\"");
outputMustBe("1\"2\\3/4\b5\f6\n7\r8\t9");
}

private:

void whenInputIs(const char* input)
{
strcpy(json, input);
actual = parser.parse(json);
}

void outputMustBe(const char* expected)
{
Assert::AreEqual(expected, actual);
}
};
}
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ It has been written with Arduino in mind, but it isn't linked to Arduino librari
Features
--------

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

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

"Parser size" was measured with a program parsing `{"sensor":"outdoor","value":25.6}`.
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).
Expand Down

0 comments on commit a1b6c2d

Please sign in to comment.