diff --git a/CHANGELOG.md b/CHANGELOG.md
index a7e67baa5..e0817055e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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:
@@ -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:
@@ -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:
diff --git a/JsonParser/JsonToken.cpp b/JsonParser/JsonToken.cpp
index 0d60dfcc4..27685e3fa 100644
--- a/JsonParser/JsonToken.cpp
+++ b/JsonParser/JsonToken.cpp
@@ -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
diff --git a/JsonParser/JsonToken.h b/JsonParser/JsonToken.h
index 2646541bb..3733a7cb8 100644
--- a/JsonParser/JsonToken.h
+++ b/JsonParser/JsonToken.h
@@ -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()
@@ -95,6 +91,9 @@ namespace ArduinoJson
private:
char* json;
jsmntok_t* token;
+
+ static char unescapeChar(char c);
+ static void unescapeString(char* s);
};
}
}
\ No newline at end of file
diff --git a/JsonParserTests/JsonArrayTests.cpp b/JsonParserTests/JsonArrayTests.cpp
index 8ce9878f1..a9c7721f0 100644
--- a/JsonParserTests/JsonArrayTests.cpp
+++ b/JsonParserTests/JsonArrayTests.cpp
@@ -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("[]");
@@ -55,8 +49,8 @@ namespace ArduinoJsonParserTests
itemMustNotExist(0);
}
- TEST_METHOD(TwoIntegers)
- {
+ TEST_METHOD(TwoIntegers)
+ {
setTokenCountTo(3);
whenInputIs("[1,2]");
@@ -66,7 +60,7 @@ namespace ArduinoJsonParserTests
itemMustBe(0, 1L);
itemMustBe(1, 2L);
itemMustNotExist(2);
- }
+ }
TEST_METHOD(TwoBooleans)
{
@@ -94,8 +88,8 @@ namespace ArduinoJsonParserTests
itemMustNotExist(2);
}
- TEST_METHOD(TwoDimensionsArray)
- {
+ TEST_METHOD(TwoDimensionsArray)
+ {
setTokenCountTo(7);
whenInputIs("[[1,2],[3,4]]");
@@ -107,7 +101,7 @@ namespace ArduinoJsonParserTests
itemMustBe(1, 0, 3L);
itemMustBe(1, 1, 4L);
itemMustNotExist(2);
- }
+ }
TEST_METHOD(ThreeDimensionsArray)
{
@@ -127,7 +121,7 @@ namespace ArduinoJsonParserTests
itemMustBe(1, 1, 1, 8L);
itemMustNotExist(2);
}
-
+
private:
void setTokenCountTo(int n)
@@ -191,5 +185,5 @@ namespace ArduinoJsonParserTests
Assert::AreEqual(0L, array.getLong(index));
Assert::IsNull(array.getString(index));
}
- };
+ };
}
\ No newline at end of file
diff --git a/JsonParserTests/JsonObjectTests.cpp b/JsonParserTests/JsonObjectTests.cpp
index 1649b032b..6b0e6be8a 100644
--- a/JsonParserTests/JsonObjectTests.cpp
+++ b/JsonParserTests/JsonObjectTests.cpp
@@ -23,12 +23,6 @@ namespace ArduinoJsonParserTests
public:
- TEST_METHOD(EmptyString)
- {
- whenInputIs("");
- parseMustFail();
- }
-
TEST_METHOD(EmptyHashTable)
{
whenInputIs("{}");
diff --git a/JsonParserTests/JsonParserTests.vcxproj b/JsonParserTests/JsonParserTests.vcxproj
index 9fbfcc882..b755c6bc8 100644
--- a/JsonParserTests/JsonParserTests.vcxproj
+++ b/JsonParserTests/JsonParserTests.vcxproj
@@ -90,6 +90,7 @@
+
diff --git a/JsonParserTests/JsonParserTests.vcxproj.filters b/JsonParserTests/JsonParserTests.vcxproj.filters
index d15e6ea3e..0034267a9 100644
--- a/JsonParserTests/JsonParserTests.vcxproj.filters
+++ b/JsonParserTests/JsonParserTests.vcxproj.filters
@@ -15,9 +15,6 @@
-
- Source Files
-
Source Files
@@ -30,5 +27,11 @@
Source Files
+
+ Source Files
+
+
+ Source Files
+
\ No newline at end of file
diff --git a/JsonParserTests/JsonStringTests.cpp b/JsonParserTests/JsonStringTests.cpp
new file mode 100644
index 000000000..55df13a0e
--- /dev/null
+++ b/JsonParserTests/JsonStringTests.cpp
@@ -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);
+ }
+ };
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index ee5221a6f..c1a50eb54 100644
--- a/README.md
+++ b/README.md
@@ -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
@@ -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).