forked from bblanchon/ArduinoJson
Moved all JsonParser code in a sub-folder.
This commit is contained in:
committed by
Benoît Blanchon
parent
80e0a51c15
commit
3d8b31b1ec
67
JsonParser/JsonArray.cpp
Normal file
67
JsonParser/JsonArray.cpp
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* malloc-free JSON parser for Arduino
|
||||
* Benoit Blanchon 2014 - MIT License
|
||||
*/
|
||||
|
||||
#include "JsonArray.h"
|
||||
#include "JsonHashTable.h"
|
||||
|
||||
JsonArray::JsonArray(char* json, jsmntok_t* tokens)
|
||||
: JsonObjectBase(json, tokens)
|
||||
{
|
||||
if (tokens == 0 || tokens[0].type != JSMN_ARRAY)
|
||||
makeInvalid();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns the token for the value at the specified index
|
||||
*/
|
||||
jsmntok_t* JsonArray::getToken(int index)
|
||||
{
|
||||
// sanity check
|
||||
if (json == 0 || tokens == 0 || index < 0 || index >= tokens[0].size)
|
||||
return 0;
|
||||
|
||||
// skip first token, it's the whole object
|
||||
jsmntok_t* currentToken = tokens + 1;
|
||||
|
||||
// skip all tokens before the specified index
|
||||
for (int i = 0; i < index; i++)
|
||||
{
|
||||
// move forward: current + nested tokens
|
||||
currentToken += 1 + getNestedTokenCount(currentToken);
|
||||
}
|
||||
|
||||
return currentToken;
|
||||
}
|
||||
|
||||
JsonArray JsonArray::getArray(int index)
|
||||
{
|
||||
return JsonArray(json, getToken(index));
|
||||
}
|
||||
|
||||
bool JsonArray::getBool(int index)
|
||||
{
|
||||
return getBoolFromToken(getToken(index));
|
||||
}
|
||||
|
||||
double JsonArray::getDouble(int index)
|
||||
{
|
||||
return getDoubleFromToken(getToken(index));
|
||||
}
|
||||
|
||||
JsonHashTable JsonArray::getHashTable(int index)
|
||||
{
|
||||
return JsonHashTable(json, getToken(index));
|
||||
}
|
||||
|
||||
long JsonArray::getLong(int index)
|
||||
{
|
||||
return getLongFromToken(getToken(index));
|
||||
}
|
||||
|
||||
char* JsonArray::getString(int index)
|
||||
{
|
||||
return getStringFromToken(getToken(index));
|
||||
}
|
42
JsonParser/JsonArray.h
Normal file
42
JsonParser/JsonArray.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* malloc-free JSON parser for Arduino
|
||||
* Benoit Blanchon 2014 - MIT License
|
||||
*/
|
||||
|
||||
#ifndef __JSONARRAY_H
|
||||
#define __JSONARRAY_H
|
||||
|
||||
#include "JsonObjectBase.h"
|
||||
|
||||
class JsonHashTable;
|
||||
|
||||
class JsonArray : public JsonObjectBase
|
||||
{
|
||||
template <int N>
|
||||
friend class JsonParser;
|
||||
|
||||
friend class JsonHashTable;
|
||||
|
||||
public:
|
||||
|
||||
JsonArray() {}
|
||||
|
||||
int getLength()
|
||||
{
|
||||
return tokens != 0 ? tokens[0].size : 0;
|
||||
}
|
||||
|
||||
JsonArray getArray(int index);
|
||||
bool getBool(int index);
|
||||
double getDouble(int index);
|
||||
JsonHashTable getHashTable(int index);
|
||||
long getLong(int index);
|
||||
char* getString(int index);
|
||||
|
||||
private:
|
||||
|
||||
JsonArray(char* json, jsmntok_t* tokens);
|
||||
jsmntok_t* getToken(int index);
|
||||
};
|
||||
|
||||
#endif
|
84
JsonParser/JsonHashTable.cpp
Normal file
84
JsonParser/JsonHashTable.cpp
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* malloc-free JSON parser for Arduino
|
||||
* Benoit Blanchon 2014 - MIT License
|
||||
*/
|
||||
|
||||
#include "JsonArray.h"
|
||||
#include "JsonHashTable.h"
|
||||
|
||||
#include <string.h> // for strcmp()
|
||||
|
||||
JsonHashTable::JsonHashTable(char* json, jsmntok_t* tokens)
|
||||
: JsonObjectBase(json, tokens)
|
||||
{
|
||||
if (tokens == 0 || tokens[0].type != JSMN_OBJECT)
|
||||
makeInvalid();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the token for the value associated with the specified key
|
||||
*/
|
||||
jsmntok_t* JsonHashTable::getToken(const char* desiredKey)
|
||||
{
|
||||
// sanity check
|
||||
if (json == 0 || tokens == 0 || desiredKey == 0)
|
||||
return 0;
|
||||
|
||||
// skip first token, it's the whole object
|
||||
jsmntok_t* currentToken = tokens + 1;
|
||||
|
||||
// scan each keys
|
||||
for (int i = 0; i < tokens[0].size / 2 ; i++)
|
||||
{
|
||||
// get key token string
|
||||
char* key = getStringFromToken(currentToken);
|
||||
|
||||
// compare with desired name
|
||||
if (strcmp(desiredKey, key) == 0)
|
||||
{
|
||||
// return the value token that follows the key token
|
||||
return currentToken + 1;
|
||||
}
|
||||
|
||||
// move forward: key + value + nested tokens
|
||||
currentToken += 2 + getNestedTokenCount(currentToken + 1);
|
||||
}
|
||||
|
||||
// nothing found, return NULL
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool JsonHashTable::containsKey(const char* key)
|
||||
{
|
||||
return getToken(key) != 0;
|
||||
}
|
||||
|
||||
JsonArray JsonHashTable::getArray(const char* key)
|
||||
{
|
||||
return JsonArray(json, getToken(key));
|
||||
}
|
||||
|
||||
bool JsonHashTable::getBool(const char* key)
|
||||
{
|
||||
return getBoolFromToken(getToken(key));
|
||||
}
|
||||
|
||||
double JsonHashTable::getDouble(const char* key)
|
||||
{
|
||||
return getDoubleFromToken(getToken(key));
|
||||
}
|
||||
|
||||
JsonHashTable JsonHashTable::getHashTable(const char* key)
|
||||
{
|
||||
return JsonHashTable(json, getToken(key));
|
||||
}
|
||||
|
||||
long JsonHashTable::getLong(const char* key)
|
||||
{
|
||||
return getLongFromToken(getToken(key));
|
||||
}
|
||||
|
||||
char* JsonHashTable::getString(const char* key)
|
||||
{
|
||||
return getStringFromToken(getToken(key));
|
||||
}
|
39
JsonParser/JsonHashTable.h
Normal file
39
JsonParser/JsonHashTable.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* malloc-free JSON parser for Arduino
|
||||
* Benoit Blanchon 2014 - MIT License
|
||||
*/
|
||||
|
||||
#ifndef __JSONHASHTABLE_H
|
||||
#define __JSONHASHTABLE_H
|
||||
|
||||
#include "JsonObjectBase.h"
|
||||
|
||||
class JsonArray;
|
||||
|
||||
class JsonHashTable : public JsonObjectBase
|
||||
{
|
||||
template <int N>
|
||||
friend class JsonParser;
|
||||
|
||||
friend class JsonArray;
|
||||
|
||||
public:
|
||||
|
||||
JsonHashTable() {}
|
||||
|
||||
bool containsKey(const char* key);
|
||||
|
||||
JsonArray getArray(const char* key);
|
||||
bool getBool(const char* key);
|
||||
double getDouble(const char* key);
|
||||
JsonHashTable getHashTable(const char* key);
|
||||
long getLong(const char* key);
|
||||
char* getString(const char* key);
|
||||
|
||||
private:
|
||||
|
||||
JsonHashTable(char* json, jsmntok_t* tokens);
|
||||
jsmntok_t* getToken(const char* key);
|
||||
};
|
||||
|
||||
#endif
|
67
JsonParser/JsonObjectBase.cpp
Normal file
67
JsonParser/JsonObjectBase.cpp
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* malloc-free JSON parser for Arduino
|
||||
* Benoit Blanchon 2014
|
||||
* MIT License
|
||||
*/
|
||||
|
||||
#include "JsonObjectBase.h"
|
||||
|
||||
#include <stdlib.h> // for strtol, strtod
|
||||
|
||||
int JsonObjectBase::getNestedTokenCount(jsmntok_t* token)
|
||||
{
|
||||
int end = token->end;
|
||||
int count = 0;
|
||||
|
||||
token++;
|
||||
|
||||
while (token->start < end)
|
||||
{
|
||||
token++;
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
bool JsonObjectBase::getBoolFromToken(jsmntok_t* token)
|
||||
{
|
||||
if (token->type != JSMN_PRIMITIVE) return 0;
|
||||
|
||||
// "true"
|
||||
if (json[token->start] == 't') return true;
|
||||
|
||||
// "false"
|
||||
if (json[token->start] == 'f') return false;
|
||||
|
||||
// "null"
|
||||
if (json[token->start] == 'n') return false;
|
||||
|
||||
// number
|
||||
return strtol(json + token->start, 0, 0) != 0;
|
||||
}
|
||||
|
||||
double JsonObjectBase::getDoubleFromToken(jsmntok_t* token)
|
||||
{
|
||||
if (token == 0 || token->type != JSMN_PRIMITIVE) return 0;
|
||||
|
||||
return strtod(json + token->start, 0);
|
||||
}
|
||||
|
||||
long JsonObjectBase::getLongFromToken(jsmntok_t* token)
|
||||
{
|
||||
if (token == 0 || token->type != JSMN_PRIMITIVE) return 0;
|
||||
|
||||
return strtol(json + token->start, 0, 0);
|
||||
}
|
||||
|
||||
char* JsonObjectBase::getStringFromToken(jsmntok_t* token)
|
||||
{
|
||||
if (token == 0 || token->type != JSMN_PRIMITIVE && token->type != JSMN_STRING)
|
||||
return 0;
|
||||
|
||||
// add null terminator to the string
|
||||
json[token->end] = 0;
|
||||
|
||||
return json + token->start;
|
||||
}
|
52
JsonParser/JsonObjectBase.h
Normal file
52
JsonParser/JsonObjectBase.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* malloc-free JSON parser for Arduino
|
||||
* Benoit Blanchon 2014
|
||||
* MIT License
|
||||
*/
|
||||
|
||||
#ifndef __JSONOBJECTBASE_H
|
||||
#define __JSONOBJECTBASE_H
|
||||
|
||||
#include "jsmn.h"
|
||||
|
||||
class JsonObjectBase
|
||||
{
|
||||
public:
|
||||
|
||||
JsonObjectBase()
|
||||
{
|
||||
makeInvalid();
|
||||
}
|
||||
|
||||
bool success()
|
||||
{
|
||||
return json != 0 && tokens != 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
JsonObjectBase(char* json, jsmntok_t* tokens)
|
||||
{
|
||||
this->json = json;
|
||||
this->tokens = tokens;
|
||||
}
|
||||
|
||||
void makeInvalid()
|
||||
{
|
||||
json = 0;
|
||||
tokens = 0;
|
||||
}
|
||||
|
||||
static int getNestedTokenCount(jsmntok_t* token);
|
||||
|
||||
bool getBoolFromToken(jsmntok_t* token);
|
||||
double getDoubleFromToken(jsmntok_t* token);
|
||||
long getLongFromToken(jsmntok_t* token);
|
||||
char* getStringFromToken(jsmntok_t* token);
|
||||
|
||||
char* json;
|
||||
jsmntok_t* tokens;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
69
JsonParser/JsonParser.h
Normal file
69
JsonParser/JsonParser.h
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* malloc-free JSON parser for Arduino
|
||||
* Benoit Blanchon 2014 - MIT License
|
||||
*/
|
||||
|
||||
#ifndef __JSONPARSER_H
|
||||
#define __JSONPARSER_H
|
||||
|
||||
#include "JsonHashTable.h"
|
||||
#include "JsonArray.h"
|
||||
|
||||
/*
|
||||
* The JSON parser.
|
||||
*
|
||||
* You need to specifiy the number of token to be allocated for that parser.
|
||||
* Values from 16 to 32 are recommended.
|
||||
* The parser size will be MAX_TOKEN*8 bytes.
|
||||
* Don't forget that the memory size of standard Arduino board is only 2KB
|
||||
*
|
||||
* CAUTION: JsonArray and JsonHashTable contain pointers to tokens of the
|
||||
* JsonParser, so they need the JsonParser to be in memory to work.
|
||||
* As a result, you must not create JsonArray and JsonHashTable that have a
|
||||
* longer life that the JsonParser.
|
||||
*/
|
||||
template <int MAX_TOKENS>
|
||||
class JsonParser
|
||||
{
|
||||
public:
|
||||
|
||||
/*
|
||||
* Parse the JSON string and return a array.
|
||||
*
|
||||
* The content of the string may be altered to add '\0' at the
|
||||
* end of string tokens
|
||||
*/
|
||||
JsonArray parseArray(char* json)
|
||||
{
|
||||
return JsonArray(json, parse(json));
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the JSON string and return a array.
|
||||
*
|
||||
* The content of the string may be altered to add '\0' at the
|
||||
* end of string tokens
|
||||
*/
|
||||
JsonHashTable parseHashTable(char* json)
|
||||
{
|
||||
return JsonHashTable(json, parse(json));
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
jsmntok_t* parse(char* json)
|
||||
{
|
||||
jsmn_parser parser;
|
||||
jsmn_init(&parser);
|
||||
|
||||
if (JSMN_SUCCESS != jsmn_parse(&parser, json, tokens, MAX_TOKENS))
|
||||
return 0;
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
jsmntok_t tokens[MAX_TOKENS];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
416
JsonParser/README.md
Normal file
416
JsonParser/README.md
Normal file
@ -0,0 +1,416 @@
|
||||
An efficient JSON parser for Arduino
|
||||
====================================
|
||||
|
||||
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
|
||||
--------
|
||||
|
||||
* Based on the well-proven [jsmn](http://zserge.com/jsmn.html) tokenizer
|
||||
* Supports nested objects
|
||||
* Works with fixed memory allocation : no `malloc()`
|
||||
* Low footprint
|
||||
* MIT License
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
char json[] = "{\"Name\":\"Blanchon\",\"Skills\":[\"C\",\"C++\",\"C#\"],\"Age\":32,\"Online\":true}";
|
||||
|
||||
JsonParser<32> parser;
|
||||
|
||||
JsonHashTable hashTable = parser.parseHashTable(json);
|
||||
|
||||
if (!hashTable.success())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char* name = hashTable.getString("Name");
|
||||
|
||||
JsonArray skills = hashTable.getArray("Skills");
|
||||
|
||||
int age = hashTable.getLong("Age");
|
||||
|
||||
bool online = hashTable.getBool("Online");
|
||||
|
||||
|
||||
|
||||
How to use ?
|
||||
-------------
|
||||
|
||||
### 1. Install the library
|
||||
|
||||
Download the library and extract it to:
|
||||
|
||||
<your Arduino Sketch folder>/libraries/ArduinoJsonParser
|
||||
|
||||
### 2. Import in your sketch
|
||||
|
||||
Just add the following line on the top of your `.ino` file:
|
||||
|
||||
#include <JsonParser.h>
|
||||
|
||||
### 3. Create a parser
|
||||
|
||||
To extract data from the JSON string, you need to create a `JsonParser`, and specify the number of token you allocate for the parser itself:
|
||||
|
||||
JsonParser<32> parser;
|
||||
|
||||
> #### How to choose the number of tokens ?
|
||||
|
||||
> 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 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 RAM and it's probably big.
|
||||
|
||||
> 32 tokens may seem small, but it's very decent 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 very likely.
|
||||
|
||||
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[]` containing to the following JSON string:
|
||||
|
||||
{
|
||||
"Name":"Blanchon",
|
||||
"Skills":[
|
||||
"C",
|
||||
"C++",
|
||||
"C#"],
|
||||
"Age":32,
|
||||
"Online":true
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
To check if the parsing was successful, you must check:
|
||||
|
||||
if (!root.success())
|
||||
{
|
||||
// Parsing fail: could be an invalid JSON, or too many tokens
|
||||
}
|
||||
|
||||
And then extract the member you need:
|
||||
|
||||
char* name = hashTable.getString("Name");
|
||||
|
||||
JsonArray skills = hashTable.getArray("Skills");
|
||||
|
||||
int age = hashTable.getLong("Age");
|
||||
|
||||
bool online = hashTable.getBool("Online");
|
||||
|
||||
#### Array
|
||||
|
||||
Consider we have a `char json[]` containing to the following JSON string:
|
||||
|
||||
[
|
||||
[ 1.2, 3.4 ],
|
||||
[ 5.6, 7.8 ]
|
||||
]
|
||||
|
||||
In this case the root object of the JSON string is an array, so you need to extract a `JsonArray`:
|
||||
|
||||
JsonArray root = parser.parseArray(json);
|
||||
|
||||
To check if the parsing was successful, you must check:
|
||||
|
||||
if (!root.success())
|
||||
{
|
||||
// Parsing fail: could be an invalid JSON, or too many tokens
|
||||
}
|
||||
|
||||
And then extract the content by its index in the array:
|
||||
|
||||
JsonArray row0 = root.getArray(0);
|
||||
double a = row0.getDouble(0);
|
||||
|
||||
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.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
As you'll see the code size if between 1680 and 3528 bytes, depending on the features you use.
|
||||
|
||||
### Minimum setup
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Function</th>
|
||||
<th>Size in bytes</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>strcmp(char*,char*)</td>
|
||||
<td>18</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>jsmn_init(jsmn_parser*)</td>
|
||||
<td>20</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>jsmn_parse(jsmn_parser*, char const*, jsmntok_t*, unsigned int)</td>
|
||||
<td>960</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>JsonParser::parse(char*)</td>
|
||||
<td>106</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>JsonObjectBase::getNestedTokenCount(jsmntok_t*)</td>
|
||||
<td>84</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>JsonObjectBase::getStringFromToken(jsmntok_t*)</td>
|
||||
<td>68</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>JsonArray::JsonArray(char*, jsmntok_t*)</td>
|
||||
<td>42</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>JsonArray::getToken(int)</td>
|
||||
<td>112</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>JsonArray::getString(int)</td>
|
||||
<td>18</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>JsonHashTable::JsonHashTable(char*, jsmntok_t*)</td>
|
||||
<td>42</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>JsonHashTable::getToken(char*)</td>
|
||||
<td>180</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>JsonHashTable::getString(char*)</td>
|
||||
<td>18</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>TOTAL</td>
|
||||
<td>1680</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### Additional space to parse nested objects
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Function</th>
|
||||
<th>Size in bytes</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>JsonArray::getArray(int)</td>
|
||||
<td>42</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>JsonArray::getHashTable(int)</td>
|
||||
<td>64</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>JsonHashTable::getArray(char*)</td>
|
||||
<td>64</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>JsonHashTable::getHashTable(char*)</td>
|
||||
<td>42</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>TOTAL</td>
|
||||
<td>212</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### Additional space to parse `bool` values
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Function</th>
|
||||
<th>Size in bytes</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>JsonObjectBase::getBoolFromToken(jsmntok_t*)</td>
|
||||
<td>82</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>JsonArray::getBool(int)</td>
|
||||
<td>18</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>JsonHashTable::getBool(char*)</td>
|
||||
<td>18</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>TOTAL</td>
|
||||
<td>130</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### Additional space to parse `double` values
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Function</th>
|
||||
<th>Size in bytes</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>strtod(char*,int)</td>
|
||||
<td>704</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>JsonObjectBase::getDoubleFromToken(jsmntok_t*)</td>
|
||||
<td>44</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>JsonArray::getDouble(int)</td>
|
||||
<td>18</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>JsonHashTable::getDouble(char*)</td>
|
||||
<td>18</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>TOTAL</td>
|
||||
<td>796</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### Additional space to parse `long` values
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Function</th>
|
||||
<th>Size in bytes</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>strtol(char*,char**,int)</td>
|
||||
<td>606</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>JsonObjectBase::getLongFromToken(jsmntok_t*)</td>
|
||||
<td>56</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>JsonArray::getLong(int)</td>
|
||||
<td>18</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>JsonHashTable::getLong(char*)</td>
|
||||
<td>18</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>TOTAL</td>
|
||||
<td>710</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
Links
|
||||
-----
|
||||
|
||||
* [The project for which I made me this library](http://blog.benoitblanchon.fr/rfid-payment-terminal/)
|
||||
* [Blog post on the motivation for this library](http://blog.benoitblanchon.fr/arduino-json-parser/)
|
255
JsonParser/jsmn.cpp
Normal file
255
JsonParser/jsmn.cpp
Normal file
@ -0,0 +1,255 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "jsmn.h"
|
||||
|
||||
/**
|
||||
* Allocates a fresh unused token from the token pull.
|
||||
*/
|
||||
static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
|
||||
jsmntok_t *tokens, size_t num_tokens) {
|
||||
jsmntok_t *tok;
|
||||
if (parser->toknext >= num_tokens) {
|
||||
return NULL;
|
||||
}
|
||||
tok = &tokens[parser->toknext++];
|
||||
tok->start = tok->end = -1;
|
||||
tok->size = 0;
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
tok->parent = -1;
|
||||
#endif
|
||||
return tok;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills token type and boundaries.
|
||||
*/
|
||||
static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
|
||||
int start, int end) {
|
||||
token->type = type;
|
||||
token->start = start;
|
||||
token->end = end;
|
||||
token->size = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills next available token with JSON primitive.
|
||||
*/
|
||||
static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js,
|
||||
jsmntok_t *tokens, size_t num_tokens) {
|
||||
jsmntok_t *token;
|
||||
int start;
|
||||
|
||||
start = parser->pos;
|
||||
|
||||
for (; js[parser->pos] != '\0'; parser->pos++) {
|
||||
switch (js[parser->pos]) {
|
||||
#ifndef JSMN_STRICT
|
||||
/* In strict mode primitive must be followed by "," or "}" or "]" */
|
||||
case ':':
|
||||
#endif
|
||||
case '\t' : case '\r' : case '\n' : case ' ' :
|
||||
case ',' : case ']' : case '}' :
|
||||
goto found;
|
||||
}
|
||||
if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
}
|
||||
#ifdef JSMN_STRICT
|
||||
/* In strict mode primitive must be followed by a comma/object/array */
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_PART;
|
||||
#endif
|
||||
|
||||
found:
|
||||
token = jsmn_alloc_token(parser, tokens, num_tokens);
|
||||
if (token == NULL) {
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_NOMEM;
|
||||
}
|
||||
jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
token->parent = parser->toksuper;
|
||||
#endif
|
||||
parser->pos--;
|
||||
return JSMN_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filsl next token with JSON string.
|
||||
*/
|
||||
static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js,
|
||||
jsmntok_t *tokens, size_t num_tokens) {
|
||||
jsmntok_t *token;
|
||||
|
||||
int start = parser->pos;
|
||||
|
||||
parser->pos++;
|
||||
|
||||
/* Skip starting quote */
|
||||
for (; js[parser->pos] != '\0'; parser->pos++) {
|
||||
char c = js[parser->pos];
|
||||
|
||||
/* Quote: end of string */
|
||||
if (c == '\"') {
|
||||
token = jsmn_alloc_token(parser, tokens, num_tokens);
|
||||
if (token == NULL) {
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_NOMEM;
|
||||
}
|
||||
jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
token->parent = parser->toksuper;
|
||||
#endif
|
||||
return JSMN_SUCCESS;
|
||||
}
|
||||
|
||||
/* Backslash: Quoted symbol expected */
|
||||
if (c == '\\') {
|
||||
parser->pos++;
|
||||
switch (js[parser->pos]) {
|
||||
/* Allowed escaped symbols */
|
||||
case '\"': case '/' : case '\\' : case 'b' :
|
||||
case 'f' : case 'r' : case 'n' : case 't' :
|
||||
break;
|
||||
/* Allows escaped symbol \uXXXX */
|
||||
case 'u':
|
||||
/* TODO */
|
||||
break;
|
||||
/* Unexpected symbol */
|
||||
default:
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_PART;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse JSON string and fill tokens.
|
||||
*/
|
||||
jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, jsmntok_t *tokens,
|
||||
unsigned int num_tokens) {
|
||||
jsmnerr_t r;
|
||||
int i;
|
||||
jsmntok_t *token;
|
||||
|
||||
for (; js[parser->pos] != '\0'; parser->pos++) {
|
||||
char c;
|
||||
jsmntype_t type;
|
||||
|
||||
c = js[parser->pos];
|
||||
switch (c) {
|
||||
case '{': case '[':
|
||||
token = jsmn_alloc_token(parser, tokens, num_tokens);
|
||||
if (token == NULL)
|
||||
return JSMN_ERROR_NOMEM;
|
||||
if (parser->toksuper != -1) {
|
||||
tokens[parser->toksuper].size++;
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
token->parent = parser->toksuper;
|
||||
#endif
|
||||
}
|
||||
token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
|
||||
token->start = parser->pos;
|
||||
parser->toksuper = parser->toknext - 1;
|
||||
break;
|
||||
case '}': case ']':
|
||||
type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
if (parser->toknext < 1) {
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
token = &tokens[parser->toknext - 1];
|
||||
for (;;) {
|
||||
if (token->start != -1 && token->end == -1) {
|
||||
if (token->type != type) {
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
token->end = parser->pos + 1;
|
||||
parser->toksuper = token->parent;
|
||||
break;
|
||||
}
|
||||
if (token->parent == -1) {
|
||||
break;
|
||||
}
|
||||
token = &tokens[token->parent];
|
||||
}
|
||||
#else
|
||||
for (i = parser->toknext - 1; i >= 0; i--) {
|
||||
token = &tokens[i];
|
||||
if (token->start != -1 && token->end == -1) {
|
||||
if (token->type != type) {
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
parser->toksuper = -1;
|
||||
token->end = parser->pos + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Error if unmatched closing bracket */
|
||||
if (i == -1) return JSMN_ERROR_INVAL;
|
||||
for (; i >= 0; i--) {
|
||||
token = &tokens[i];
|
||||
if (token->start != -1 && token->end == -1) {
|
||||
parser->toksuper = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case '\"':
|
||||
r = jsmn_parse_string(parser, js, tokens, num_tokens);
|
||||
if (r < 0) return r;
|
||||
if (parser->toksuper != -1)
|
||||
tokens[parser->toksuper].size++;
|
||||
break;
|
||||
case '\t' : case '\r' : case '\n' : case ':' : case ',': case ' ':
|
||||
break;
|
||||
#ifdef JSMN_STRICT
|
||||
/* In strict mode primitives are: numbers and booleans */
|
||||
case '-': case '0': case '1' : case '2': case '3' : case '4':
|
||||
case '5': case '6': case '7' : case '8': case '9':
|
||||
case 't': case 'f': case 'n' :
|
||||
#else
|
||||
/* In non-strict mode every unquoted value is a primitive */
|
||||
default:
|
||||
#endif
|
||||
r = jsmn_parse_primitive(parser, js, tokens, num_tokens);
|
||||
if (r < 0) return r;
|
||||
if (parser->toksuper != -1)
|
||||
tokens[parser->toksuper].size++;
|
||||
break;
|
||||
|
||||
#ifdef JSMN_STRICT
|
||||
/* Unexpected char in strict mode */
|
||||
default:
|
||||
return JSMN_ERROR_INVAL;
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for (i = parser->toknext - 1; i >= 0; i--) {
|
||||
/* Unmatched opened object or array */
|
||||
if (tokens[i].start != -1 && tokens[i].end == -1) {
|
||||
return JSMN_ERROR_PART;
|
||||
}
|
||||
}
|
||||
|
||||
return JSMN_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new parser based over a given buffer with an array of tokens
|
||||
* available.
|
||||
*/
|
||||
void jsmn_init(jsmn_parser *parser) {
|
||||
parser->pos = 0;
|
||||
parser->toknext = 0;
|
||||
parser->toksuper = -1;
|
||||
}
|
||||
|
67
JsonParser/jsmn.h
Normal file
67
JsonParser/jsmn.h
Normal file
@ -0,0 +1,67 @@
|
||||
#ifndef __JSMN_H_
|
||||
#define __JSMN_H_
|
||||
|
||||
/**
|
||||
* JSON type identifier. Basic types are:
|
||||
* o Object
|
||||
* o Array
|
||||
* o String
|
||||
* o Other primitive: number, boolean (true/false) or null
|
||||
*/
|
||||
typedef enum {
|
||||
JSMN_PRIMITIVE = 0,
|
||||
JSMN_OBJECT = 1,
|
||||
JSMN_ARRAY = 2,
|
||||
JSMN_STRING = 3
|
||||
} jsmntype_t;
|
||||
|
||||
typedef enum {
|
||||
/* Not enough tokens were provided */
|
||||
JSMN_ERROR_NOMEM = -1,
|
||||
/* Invalid character inside JSON string */
|
||||
JSMN_ERROR_INVAL = -2,
|
||||
/* The string is not a full JSON packet, more bytes expected */
|
||||
JSMN_ERROR_PART = -3,
|
||||
/* Everything was fine */
|
||||
JSMN_SUCCESS = 0
|
||||
} jsmnerr_t;
|
||||
|
||||
/**
|
||||
* JSON token description.
|
||||
* @param type type (object, array, string etc.)
|
||||
* @param start start position in JSON data string
|
||||
* @param end end position in JSON data string
|
||||
*/
|
||||
typedef struct {
|
||||
jsmntype_t type;
|
||||
int start;
|
||||
int end;
|
||||
int size;
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
int parent;
|
||||
#endif
|
||||
} jsmntok_t;
|
||||
|
||||
/**
|
||||
* JSON parser. Contains an array of token blocks available. Also stores
|
||||
* the string being parsed now and current position in that string
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned int pos; /* offset in the JSON string */
|
||||
int toknext; /* next token to allocate */
|
||||
int toksuper; /* superior token node, e.g parent object or array */
|
||||
} jsmn_parser;
|
||||
|
||||
/**
|
||||
* Create JSON parser over an array of tokens
|
||||
*/
|
||||
void jsmn_init(jsmn_parser *parser);
|
||||
|
||||
/**
|
||||
* Run JSON parser. It parses a JSON data string into and array of tokens, each describing
|
||||
* a single JSON object.
|
||||
*/
|
||||
jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js,
|
||||
jsmntok_t *tokens, unsigned int num_tokens);
|
||||
|
||||
#endif /* __JSMN_H_ */
|
Reference in New Issue
Block a user