forked from bblanchon/ArduinoJson
Added "Common pitfall" section
This commit is contained in:
421
README.md
421
README.md
@ -1,12 +1,18 @@
|
|||||||
# 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'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.
|
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.
|
||||||
|
|
||||||
## Example
|
|
||||||
|
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
|
||||||
char* json = "{\"Name\":\"Blanchon\",\"Skills\":[\"C\",\"C++\",\"C#\"],\"Age\":32,\"Online\":true}";
|
char* json = "{\"Name\":\"Blanchon\",\"Skills\":[\"C\",\"C++\",\"C#\"],\"Age\":32,\"Online\":true}";
|
||||||
|
|
||||||
@ -27,19 +33,23 @@ It has been written with Arduino in mind, but it isn't linked to Arduino librari
|
|||||||
|
|
||||||
bool online = hashTable.getBool("Online");
|
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:
|
Download the library and extract it to:
|
||||||
|
|
||||||
<your Arduino Sketch folder>/libraries/ArduinoJonsParser
|
<your Arduino Sketch folder>/libraries/ArduinoJsonParser
|
||||||
|
|
||||||
### 2. Import in your sketch
|
### 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 <JonsParser.h>
|
#include <JsonParser.h>
|
||||||
|
|
||||||
### 3. Create a parser
|
### 3. Create a parser
|
||||||
|
|
||||||
@ -49,20 +59,25 @@ To extract data from the JSON string, you need to create a `JsonParser`, and spe
|
|||||||
|
|
||||||
> #### How to choose the number of tokens ?
|
> #### 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).
|
> 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.
|
> 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.
|
> 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
|
### 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:
|
Consider we have a `char* json` pointing to the following JSON string:
|
||||||
|
|
||||||
@ -76,7 +91,7 @@ Consider we have a `char* json` pointing to the following JSON string:
|
|||||||
"Online":true
|
"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);
|
JsonHashTable root = parser.parseHashTable(json);
|
||||||
|
|
||||||
@ -126,7 +141,95 @@ or simply:
|
|||||||
|
|
||||||
double a = root.getArray(0).getDouble(0);
|
double a = root.getArray(0).getDouble(0);
|
||||||
|
|
||||||
## Code size
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Size in bytes</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Parser<N></td>
|
||||||
|
<td>8 x N</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>JsonArray</td>
|
||||||
|
<td>4</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>JsonHashTable</td>
|
||||||
|
<td>4</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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.
|
Theses tables has been created by analyzing the map file generated by AVR-GCC after adding `-Wl,-Map,foo.map` to the command line.
|
||||||
|
|
||||||
@ -135,168 +238,168 @@ As you'll see the code size if between 1680 and 3528 bytes, depending on the fea
|
|||||||
### Minimum setup
|
### Minimum setup
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Function</th>
|
<th>Function</th>
|
||||||
<th>Size in bytes</th>
|
<th>Size in bytes</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>strcmp(char*,char*)</td>
|
<td>strcmp(char*,char*)</td>
|
||||||
<td>18</td>
|
<td>18</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>jsmn_init(jsmn_parser*)</td>
|
<td>jsmn_init(jsmn_parser*)</td>
|
||||||
<td>20</td>
|
<td>20</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>JsonParser::parse(char*)</td>
|
<td>JsonParser::parse(char*)</td>
|
||||||
<td>106</td>
|
<td>106</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>JsonObjectBase::getNestedTokenCount(jsmntok_t*)</td>
|
<td>JsonObjectBase::getNestedTokenCount(jsmntok_t*)</td>
|
||||||
<td>84</td>
|
<td>84</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>JsonObjectBase::getStringFromToken(jsmntok_t*)</td>
|
<td>JsonObjectBase::getStringFromToken(jsmntok_t*)</td>
|
||||||
<td>68</td>
|
<td>68</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>JsonArray::JsonArray(char*, jsmntok_t*)</td>
|
<td>JsonArray::JsonArray(char*, jsmntok_t*)</td>
|
||||||
<td>42</td>
|
<td>42</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>JsonArray::getToken(int)</td>
|
<td>JsonArray::getToken(int)</td>
|
||||||
<td>112</td>
|
<td>112</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>JsonArray::getString(int)</td>
|
<td>JsonArray::getString(int)</td>
|
||||||
<td>18</td>
|
<td>18</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>JsonHashTable::JsonHashTable(char*, jsmntok_t*)</td>
|
<td>JsonHashTable::JsonHashTable(char*, jsmntok_t*)</td>
|
||||||
<td>42</td>
|
<td>42</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>JsonHashTable::getToken(char*)</td>
|
<td>JsonHashTable::getToken(char*)</td>
|
||||||
<td>180</td>
|
<td>180</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>JsonHashTable::getString(char*)</td>
|
<td>JsonHashTable::getString(char*)</td>
|
||||||
<td>18</td>
|
<td>18</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>TOTAL</td>
|
<td>TOTAL</td>
|
||||||
<td>1680</td>
|
<td>1680</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
### Additional space to parse nested objects
|
### Additional space to parse nested objects
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Function</th>
|
<th>Function</th>
|
||||||
<th>Size in bytes</th>
|
<th>Size in bytes</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>JsonArray::getArray(int)</td>
|
<td>JsonArray::getArray(int)</td>
|
||||||
<td>42</td>
|
<td>42</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>JsonArray::getHashTable(int)</td>
|
<td>JsonArray::getHashTable(int)</td>
|
||||||
<td>64</td>
|
<td>64</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>JsonHashTable::getArray(char*)</td>
|
<td>JsonHashTable::getArray(char*)</td>
|
||||||
<td>64</td>
|
<td>64</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>JsonHashTable::getHashTable(char*)</td>
|
<td>JsonHashTable::getHashTable(char*)</td>
|
||||||
<td>42</td>
|
<td>42</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>TOTAL</td>
|
<td>TOTAL</td>
|
||||||
<td>212</td>
|
<td>212</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
### Additional space to parse `bool` values
|
### Additional space to parse `bool` values
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Function</th>
|
<th>Function</th>
|
||||||
<th>Size in bytes</th>
|
<th>Size in bytes</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>JsonObjectBase::getBoolFromToken(jsmntok_t*)</td>
|
<td>JsonObjectBase::getBoolFromToken(jsmntok_t*)</td>
|
||||||
<td>82</td>
|
<td>82</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>JsonArray::getBool(int)</td>
|
<td>JsonArray::getBool(int)</td>
|
||||||
<td>18</td>
|
<td>18</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>JsonHashTable::getBool(char*)</td>
|
<td>JsonHashTable::getBool(char*)</td>
|
||||||
<td>18</td>
|
<td>18</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>TOTAL</td>
|
<td>TOTAL</td>
|
||||||
<td>130</td>
|
<td>130</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
### Additional space to parse `double` values
|
### Additional space to parse `double` values
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Function</th>
|
<th>Function</th>
|
||||||
<th>Size in bytes</th>
|
<th>Size in bytes</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>strtod(char*,int)</td>
|
<td>strtod(char*,int)</td>
|
||||||
<td>704</td>
|
<td>704</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>JsonObjectBase::getDoubleFromToken(jsmntok_t*)</td>
|
<td>JsonObjectBase::getDoubleFromToken(jsmntok_t*)</td>
|
||||||
<td>44</td>
|
<td>44</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>JsonArray::getDouble(int)</td>
|
<td>JsonArray::getDouble(int)</td>
|
||||||
<td>18</td>
|
<td>18</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>JsonHashTable::getDouble(char*)</td>
|
<td>JsonHashTable::getDouble(char*)</td>
|
||||||
<td>18</td>
|
<td>18</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>TOTAL</td>
|
<td>TOTAL</td>
|
||||||
<td>796</td>
|
<td>796</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
### Additional space to parse `long` values
|
### Additional space to parse `long` values
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Function</th>
|
<th>Function</th>
|
||||||
<th>Size in bytes</th>
|
<th>Size in bytes</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>strtol(char*,char**,int)</td>
|
<td>strtol(char*,char**,int)</td>
|
||||||
<td>606</td>
|
<td>606</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>JsonObjectBase::getLongFromToken(jsmntok_t*)</td>
|
<td>JsonObjectBase::getLongFromToken(jsmntok_t*)</td>
|
||||||
<td>56</td>
|
<td>56</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>JsonArray::getLong(int)</td>
|
<td>JsonArray::getLong(int)</td>
|
||||||
<td>18</td>
|
<td>18</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>JsonHashTable::getLong(char*)</td>
|
<td>JsonHashTable::getLong(char*)</td>
|
||||||
<td>18</td>
|
<td>18</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>TOTAL</td>
|
<td>TOTAL</td>
|
||||||
<td>710</td>
|
<td>710</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
Reference in New Issue
Block a user