diff --git a/README.md b/README.md index 4e54e7d6..d7c912f2 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,16 @@ -# A malloc-free JSON parser for Arduino +A malloc-free JSON parser for Arduino +===================================== -The library is an thin C++ wrapper around the *jsmn* tokenizer: http://zserge.com/jsmn.html + +This library is an thin C++ wrapper around the *jsmn* tokenizer: http://zserge.com/jsmn.html It's design to be very lightweight, works without any allocation on the heap (no malloc) and supports nested objects. It has been written with Arduino in mind, but it isn't linked to Arduino libraries so you can use this library on any other C++ project. -## Features + +Features +------- * Based on the well-proven [jsmn](http://zserge.com/jsmn.html) tokenizer * Supports nested objects @@ -14,7 +18,9 @@ It has been written with Arduino in mind, but it isn't linked to Arduino librari * Low footprint * MIT License -## Example + +Example +------- char* json = "{\"Name\":\"Blanchon\",\"Skills\":[\"C\",\"C++\",\"C#\"],\"Age\":32,\"Online\":true}"; @@ -35,19 +41,23 @@ It has been written with Arduino in mind, but it isn't linked to Arduino librari bool online = hashTable.getBool("Online"); -## How to use ? -### 1. Install the the library + + +How to use ? +------------- + +### 1. Install the library Download the library and extract it to: - /libraries/ArduinoJonsParser + /libraries/ArduinoJsonParser ### 2. Import in your sketch -Just add the following line on the to of your `.ino` file: +Just add the following line on the top of your `.ino` file: - #include + #include ### 3. Create a parser @@ -57,20 +67,25 @@ To extract data from the JSON string, you need to create a `JsonParser`, and spe > #### How to choose the number of tokens ? -> First you need to know exactly what a token is. A token is an element af the JSON object: either a key, a value, an hash-table or an array. +> A token is an element of the JSON object: either a key, a value, an hash-table or an array. > As an example the `char* json` on the top of this page contains 12 tokens (don't forget to count 1 for the whole object and 1 more for the array itself). -> The more tokens you allocate, the more complex the JSON can be, but also the more memory will be occupied. +> The more tokens you allocate, the more complex the JSON can be, but also the more memory is occupied. > Each token takes 8 bytes, so `sizeof(JsonParser<32>)` is 256 bytes which is quite big in an Arduino with only 2KB of RAM. -> Don't forget that you also have to store the JSON string in memory and it's probably big. +> Don't forget that you also have to store the JSON string in RAM and it's probably big. > 32 tokens may seem small but it's very descent for an 8-bit processor, you wouldn't get better results with other JSON libraries. ### 4. Extract data -To use this library, you need to know beforehand what is the type of data contained in the JSON string, which is extremely likely. +To use this library, you need to know beforehand what is the type of data contained in the JSON string, which is very likely. -#### Hash table +The root object has to be either a hash-table (like `{"key":"value"}`) or an array (like `[1,2]`). + +The nested objects can be either arrays, booleans, hash-tables, numbers or strings. +If you need other type, you can get the string value and parse it yourself. + +#### Hash-table Consider we have a `char* json` pointing to the following JSON string: @@ -84,7 +99,7 @@ Consider we have a `char* json` pointing to the following JSON string: "Online":true } -In this case the root object of the JSON string is a hash table, so you need to extract a `JsonHashTable`: +In this case the root object of the JSON string is a hash-table, so you need to extract a `JsonHashTable`: JsonHashTable root = parser.parseHashTable(json); @@ -133,8 +148,96 @@ And then extract the content by its index in the array: or simply: double a = root.getArray(0).getDouble(0); + + + +Common pitfalls +--------------- + +### 1. Not enough tokens + +By design, the library has no way to tell you why `JsonParser::parseArray()` or `JsonParser::parseHashTable()` failed. + +There are basically two reasons why they may fail: + +1. the JSON string is invalid +2. the JSON string contains more tokens that the parser can store + +So, if you are sure the JSON string is correct and you still can't parse it, you should slightly increase the number of token of the parser. + +### 2. Not enough memory + +You may go into unpredictable trouble if you allocate more memory than your processor really has. +It's a very common issue in embedded development. + +To diagnose this, look at every big objects in you code and sum their size to check that they fit in RAM. + +For example, don't do this: + + char json[1024]; // 1 KB + JsonParser<64> parser; // 512 B + +because it may be too big for a processor with only 2 KB: you need free memory to store other variables and the call stack. + +That is why an 8-bit processor is not able to parse long and complex JSON strings. + +### 3. JsonParser not in memory + +To reduce the memory consumption, `JsonArray` and `JsonHashTable` contains pointer to the token that are inside the `JsonParser`. This can only work if the `JsonParser` is still in memory. + +For example, don't do this: + + JsonArray getArray(char* json) + { + JsonParser<16> parser; + return parser.parseArray(parser); + } + +because the local variable `parser` will be *removed* from memory when the function `getArray()` returns, and the pointer inside `JsonArray` will point to an invalid location. + +### 4. JSON string is altered + +This will probably never be an issue, but you need to be aware of this feature. + +When you pass a `char*` to `JsonParser::parseArray()` or `JsonParser::parseHashTable()`, the content of the string will be altered to add `\0` at the end of the tokens. + +This is because we want functions like `JsonArray::getString()` to return a null-terminating string without any memory allocation. -## Code size + + + +Memory usage +------------ + +Here are the size of the main classes of the library. + +This table is for an 8-bit Arduino, types would be bigger on a 32-bit processor. + + + + + + + + + + + + + + + + + + +
TypeSize in bytes
Parser<N>8 x N
JsonArray4
JsonHashTable4
+ + + + + +Code size +--------- Theses tables has been created by analyzing the map file generated by AVR-GCC after adding `-Wl,-Map,foo.map` to the command line. @@ -143,172 +246,172 @@ As you'll see the code size if between 1680 and 3528 bytes, depending on the fea ### Minimum setup - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FunctionSize in bytes
strcmp(char*,char*)18
jsmn_init(jsmn_parser*)20
jsmn_parse(jsmn_parser*, char const*, jsmntok_t*, unsigned int)960
JsonParser::parse(char*)106
JsonObjectBase::getNestedTokenCount(jsmntok_t*)84
JsonObjectBase::getStringFromToken(jsmntok_t*)68
JsonArray::JsonArray(char*, jsmntok_t*)42
JsonArray::getToken(int)112
JsonArray::getString(int)18
JsonHashTable::JsonHashTable(char*, jsmntok_t*)42
JsonHashTable::getToken(char*)180
JsonHashTable::getString(char*)18
TOTAL1680
FunctionSize in bytes
strcmp(char*,char*)18
jsmn_init(jsmn_parser*)20
jsmn_parse(jsmn_parser*, char const*, jsmntok_t*, unsigned int)960
JsonParser::parse(char*)106
JsonObjectBase::getNestedTokenCount(jsmntok_t*)84
JsonObjectBase::getStringFromToken(jsmntok_t*)68
JsonArray::JsonArray(char*, jsmntok_t*)42
JsonArray::getToken(int)112
JsonArray::getString(int)18
JsonHashTable::JsonHashTable(char*, jsmntok_t*)42
JsonHashTable::getToken(char*)180
JsonHashTable::getString(char*)18
TOTAL1680
### Additional space to parse nested objects - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + +
FunctionSize in bytes
JsonArray::getArray(int)42
JsonArray::getHashTable(int)64
JsonHashTable::getArray(char*)64
JsonHashTable::getHashTable(char*)42
TOTAL212
FunctionSize in bytes
JsonArray::getArray(int)42
JsonArray::getHashTable(int)64
JsonHashTable::getArray(char*)64
JsonHashTable::getHashTable(char*)42
TOTAL212
### Additional space to parse `bool` values - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + +
FunctionSize in bytes
JsonObjectBase::getBoolFromToken(jsmntok_t*)82
JsonArray::getBool(int)18
JsonHashTable::getBool(char*)18
TOTAL130
FunctionSize in bytes
JsonObjectBase::getBoolFromToken(jsmntok_t*)82
JsonArray::getBool(int)18
JsonHashTable::getBool(char*)18
TOTAL130
### Additional space to parse `double` values - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + +
FunctionSize in bytes
strtod(char*,int)704
JsonObjectBase::getDoubleFromToken(jsmntok_t*)44
JsonArray::getDouble(int)18
JsonHashTable::getDouble(char*)18
TOTAL796
FunctionSize in bytes
strtod(char*,int)704
JsonObjectBase::getDoubleFromToken(jsmntok_t*)44
JsonArray::getDouble(int)18
JsonHashTable::getDouble(char*)18
TOTAL796
### Additional space to parse `long` values - - - - - - - - - - - - - - - - - - - - - - - - -
FunctionSize in bytes
strtol(char*,char**,int)606
JsonObjectBase::getLongFromToken(jsmntok_t*)56
JsonArray::getLong(int)18
JsonHashTable::getLong(char*)18
TOTAL710
\ No newline at end of file + + Function + Size in bytes + + + strtol(char*,char**,int) + 606 + + + JsonObjectBase::getLongFromToken(jsmntok_t*) + 56 + + + JsonArray::getLong(int) + 18 + + + JsonHashTable::getLong(char*) + 18 + + + TOTAL + 710 + +