JSON Library
JSON is a data interchange format that can be used to transmit data objects as human readable text. The innovaphone apps use JSON for all websocket protocols. This library can be used to encode and decode JSON in C++.
File information
Overview
JSON uses the following primitive data types.
-
string
-
integer
-
unsigned
-
boolean
-
null
JSON uses the following constructed data types.
Here are some hints how JSON messages are build up.
-
The top level element of a JSON string can be either a primitive or a constructed data type.
-
When a JSON document is created using this library, you need to add elements to the json_io instance in the order as they appear in the JSON document.
-
Strings are encoded using quotes.
"some text"
All other primitive data style are encoded without quotes.
-4
17
true
null
-
Arrays use brackets as delimiters. The elements of an array are 0..n comma-separated unnamed values.
The values can be any primitive or constructed data type.
[ "a", "b", -5, false ]
-
Objects use curly braces as delimiters. The elements of an object are 0..n comma-separated name-value-pairs.
The names use quoted encoding. The values can be any primitive or constructed data type.
{ "name": "John Doe", "age": 30, "interests": [ "programming", "tv series", "cooking" ] }
-
Line breaks and extra white space can be inserted, but is ignored by JSON decoders.
{
"name": "John Doe",
"age": 30,
"interests": [
"programming",
"tv series",
"cooking"
]
}
Note that the library does not insert any extra white space.
{"name":"John Doe","age": 30,"interests":["programming","tv series","cooking"]}
Classes
json_io
class json_io {
public:
json_io(char * buffer);
void reset();
bool decode();
dword encode();
dword encode(word handle, char * buffer);
void write(word current, char * & p, word incomplete = 0xffff);
word add_object(word base, const char * name);
word add_array(word base, const char * name);
void add_string(word base, const char * name, const char * value, dword len=0xffffffff);
void add_string(word base, const char * name, const word * value, dword len, char * & tmp);
void add_string(word base, const char * name, const word * value, char * & tmp) { add_string(base, name, value, 0xffffffff, tmp); };
void add_replace_string(word base, const char * name, const char * value, dword len=0xffffffff);
void add_int(word base, const char * name, int c, char * & tmp);
void add_unsigned(word base, const char * name, dword c, char * & tmp);
void add_long64(word base, const char * name, long64 c, char * & tmp);
void add_ulong64(word base, const char * name, ulong64 c, char * & tmp);
void add_bool(word base, const char * name, bool value);
void add_null(word base, const char * name);
void add_double(word base, const char * name, double c, char *& tmp, byte decimalPlaces = 6);
void add_printf(word base, const char * name, char * & tmp, const char * format, ...);
void add_hexstring(word base, const char * name, const byte * hex, word hex_len, char * & tmp);
void add_json(word base, const char * name, const char * value, dword len=0xffffffff);
word get_object(word base, const char * name);
word get_object(word base, word & last);
word get_array(word base, const char * name);
word get_array(word base, word & last);
const char * get_string(word base, const char * name, bool * present=0);
const char * get_string(word base, word & last, bool * present=0);
int get_int(word base, const char * name, bool * present=0);
int get_int(word base, word & last, bool * present=0);
dword get_unsigned(word base, const char * name, bool * present=0);
dword get_unsigned(word base, word & last, bool * present=0);
long64 get_long64(word base, const char * name, bool * present = 0);
long64 get_long64(word base, word & last, bool * present = 0);
ulong64 get_ulong64(word base, const char * name, bool * present = 0);
ulong64 get_ulong64(word base, word & last, bool * present = 0);
bool get_bool(word base, const char * name, bool * present=0);
bool get_bool(word base, word & last, bool * present=0);
bool get_bool_int(word base, const char * name, int & iret, byte * present=0);
double get_double(word base, const char * name, bool * present = 0);
double get_double(word base, word & last, bool * present = 0);
byte get_flags(word handle);
word to_url(word base, char * b, word l, const char * prefix = 0, bool cont = false);
// advanced functions currently not documented
word get_next(word base, word last, byte & type, byte & flags, const char * & name, const char * & info);
word get_index();
const char * get_name(word handle);
const char * get_info(word handle);
const char* get_value(word base, byte flags, const char* name, bool * present=0);
const char* get_value(word base, byte flags, word& last, bool * present=0);
char * last;
char * name_last;
char * incomplete;
};
Overview
The json_io class can be used for encoding and decoding JSON messages to or from a char buffer.
Each element has a unique numeric ID word base
. JSON_ID_ROOT
represents the root element. It can be no or a single element. Often the root is an object that holds all the other data. Normally json_io can be used as a stack variable. A typical encoding flow looks like that:
char message[512];
json_io json(message);
word base = json.add_object(JSON_ID_ROOT, 0);
// add elements
json.encode();
Decoding is usually done like that:
json_io json(message);
json.decode();
word base = recv.get_object(JSON_ID_ROOT, 0);
// read elements
There are two different methods for traversing the JSON structure. Inside objects the elements are referenced by their name.
// adding elements
json.add_string(base, "name", "John Doe");
// reading elements
const char * name = json.get_string(base, "name");
Inside arrays the elements are enumerated.
// adding elements
json.add_string(base, 0, "programming");
json.add_string(base, 0, "tv series");
json.add_string(base, 0, "cooking");
// reading elements
word last = 0;
do {
const char * interest = json.get_string(base, last);
}
while (last != JSON_ID_NONE);
Some add functions need an additional buffer for storing the values temporarily. Those buffers are called char * & tmp
in the interface.
Note that the buffer pointer reference is increased when adding an element.
char temp[128];
char * t = temp;
json.add_attrib_printf(base, "name", t, "%s %s", "John", "Doe");
json.add_unsigned(base, "age", 30, t);
Chunked decoding
Sometimes JSON objects exceed the limits of json_io, so that they cannot be decoded completely in
one step. In this case chuncked decoding can be used. Chunked decoding works if on some level
of the JSON object a list exists, either as array or as object, which each element of this
list not exceeding the limits.
The chunked decoding works with the following steps:
-
Decode the data. The decoding returns an error, because the JSON data is incomplete
-
Find the list within the decoded data using get_array or get_object
-
Walk thru the list, until an element with the flag JSON_FLAG_INCOMPLETE is encountered.
-
Do an encode incomplete of the data, using write with the incomplete parameter set to the
list (object or array). This only encodes the elements, which are not yet complete, together
with the element into which they are embedded
-
Get the next chunk and append it to the encoded data and repeat the prevous steps until
decoding does not return an error
Sample code for chunked decoding
// data is received/read in "buf" with a lenght of len. fill is initialized to 0
// and stores the length of the remaining incomplete data
if (len) {
// decode
buf[fill + len] = 0;
class json_io json(buf);
json.decode();
// get array of objects
word a = json.get_array(JSON_ID_NONE, 0);
if (a != JSON_ID_NONE) {
for (word base = json.get_object(a, 0); base != JSON_ID_NONE && !(json.get_flags(base) &JSON_FLAG_INCOMPLETE); base = json.get_object(a, base)) {
word object = json.get_object(base, "object");
// process the data
}
// write incomplete data to buf
char * x = buf;
json.write(0, x, a);
fill = x - buf;
}
}
// append next chunk to the end of the incomplete data
Recv(&buf[fill])
Public functions
json_io (constructor)
Initializes the json_io structure.
Parameters
char * buffer | The buffer for the message. |
Remarks
The buffer is mandatory for decoding. For encoding it is optional but if it's null a buffer must be passed to the encode function. Make sure it is big enough to contain the whole message.
Note that the buffer will be modified for both encoding and decoding.
reset
Resets the internal state of the json_io structure. All added or parsed elements will be cleared from the internal state.
encode (overloaded)
Encodes the data structure into a null-terminated JSON string and writes it to the buffer specified with the constructor.
Return value
dword | The size of the encoded message. |
encode (overloaded)
Encodes a subtree of the data structure into a null-terminated JSON string and writes it to the specified buffer.
Parameters
word handle | The ID of the root element of the subtree. |
char * buffer | The buffer for the message. |
Return value
dword | The size of the encoded message. |
write
This function is used internally but it can also be used to encode the data structure to a JSON string in several chunks.
Parameters
word current | The ID of the start element. Use 0 for complete data. |
char *& p | The output buffer. |
word incomplete |
The incomplete argument can be used to write only the incomplete data so that new received
data can be appended to the buffer an decoding started again. The incomplete argument
should be the handle of the descriptor of the array, with potential incomplete elements.
|
add_object
Adds an object to the structure.
Parameters
word base | The ID of the base element. |
const char * name | The name of the object. Only used when the object is inside another object. Set to 0 otherwise. |
Return value
word |
The ID of the added object. Can be used to add elements to the object.
|
Remarks
If the object shall be added to root level, use JSON_ID_ROOT as the base.
add_array
Adds an array to the structure.
Parameters
word base | The ID of the base element. |
const char * name | The name of the array. Only used when the array is inside an object. Set to 0 otherwise. |
Return value
word |
The ID of the added array. Can be used to add elements to the array.
|
Remarks
If the array shall be added to root level, use JSON_ID_ROOT as the base.
add_string (overloaded)
Adds an UTF-8 string to the structure.
Parameters
word base | The ID of the base element. |
const char * name | The name of the element. Only used when it's is inside an object. Set to 0 otherwise. |
const char * value | The value of the element. If value is 0 the element is not added. |
dword len | The number of bytes in the value buffer. Only needed if value is not null-terminated. |
add_string (overloaded)
Adds a string with 16-bit character representation to the structure.
Parameters
word base | The ID of the base element. |
const char * name | The name of the element. Only used when it's is inside an object. Set to 0 otherwise. |
const word * value | The value of the element. If value is 0 the element is not added. |
dword len | The number of bytes in the value buffer. Only needed if value is not null-terminated. |
add_replace_string
Adds or replaces an existing UTF-8 string inside an object.
Parameters
word base | The ID of the base element. |
const char * name | The name of the element (mandatory). |
const char * value | The value of the element. |
dword len | The number of bytes in the value buffer. Only needed if value is not null-terminated. |
Remarks
The function is only implemented for strings inside objects.
add_int
Adds a 32-bit integer to the structure.
Parameters
word base | The ID of the base element. |
const char * name | The name of the element. Only used when it's is inside an object. Set to 0 otherwise. |
int c | The value of the element. |
char * & tmp | The temporary buffer to store the value until it's encoded. |
add_unsigned
Adds a 32-bit unsigned integer to the structure.
Parameters
word base | The ID of the base element. |
const char * name | The name of the element. Only used when it's is inside an object. Set to 0 otherwise. |
unsigned c | The value of the element. |
char * & tmp | The temporary buffer to store the value until it's encoded. |
add_long64
Adds a 64-bit integer to the structure.
Parameters
word base | The ID of the base element. |
const char * name | The name of the element. Only used when it's is inside an object. Set to 0 otherwise. |
long64 c | The value of the element. |
char * & tmp | The temporary buffer to store the value until it's encoded. |
add_ulong64
Adds an 64-bit unsigned integer to the structure.
Parameters
word base | The ID of the base element. |
const char * name | The name of the element. Only used when it's is inside an object. Set to 0 otherwise. |
ulong64 c | The value of the element. |
char * & tmp | The temporary buffer to store the value until it's encoded. |
add_bool
Adds a boolean to the structure.
Parameters
word base | The ID of the base element. |
const char * name | The name of the element. Only used when it's is inside an object. Set to 0 otherwise. |
bool value | The value of the element. |
add_null
Adds a null value to the structure.
Parameters
word base | The ID of the base element. |
const char * name | The name of the element. Only used when it's is inside an object. Set to 0 otherwise. |
add_double
Adds a double value to the structure.
Parameters
word base | The ID of the base element. |
const char * name | The name of the element. Only used when it's is inside an object. Set to 0 otherwise. |
double c | The value of the element. |
char * & tmp | The temporary buffer to store the value until it's encoded. |
byte decimalPlaces = 6 | The decimal places after the period. |
add_printf
Does an sprintf to a temporary buffer and adds the resulting string to the structure.
Parameters
word base | The ID of the base element. |
const char * name | The name of the element. Only used when it's is inside an object. Set to 0 otherwise. |
char * & tmp | The temporary buffer to store the value until it's encoded. |
const char * format | A standard sprintf format string. |
... | Additional parameters to be used by sprintf as defined in the format string. |
add_hexstring
Converts a binary buffer to a hex string and adds it to the structure, encoded as a JSON string.
Parameters
word base | The ID of the base element. |
const char * name | The name of the element. Only used when it's is inside an object. Set to 0 otherwise. |
const byte * hex | Arbitrary binary data. |
word hex_len | The number of bytes in the hex buffer. |
char * & tmp | The temporary buffer to store the value until it's encoded. |
add_json
Adds a raw JSON string to the structure. The string must have valid encoding. It will not be escaped by the library.
Parameters
word base | The ID of the base element. |
const char * name | The name of the element. Only used when it's is inside an object. Set to 0 otherwise. |
const char * value | The JSON string to be added. |
dword len | The number of bytes in value. Only needed if value is not null-terminated. |
get_object (overloaded)
Gets an object by name.
Parameters
word base | The ID of the base element. |
const char * name | The name of the element. Only used when it's is inside an object. Set to 0 otherwise. |
Return value
get_object (overloaded)
Gets the next object from an array.
Parameters
word base | The ID of the base element. |
word & last | A word reference that must have the value 0 for the first call. The value will be updated by the function call for each subsequent call. If the value is JSON_ID_NONE the end of the array is reached. |
Return value
word | The ID of the object or JSON_ID_NONE if there are no more objects in the array. |
get_array (overloaded)
Gets an array by name.
Parameters
word base | The ID of the base element. |
const char * name | The name of the element. Only used when it's is inside an object. Set to 0 otherwise. |
Return value
get_array (overloaded)
Gets the next array from an array.
Parameters
word base | The ID of the base element. |
word & last | A word reference that must have the value 0 for the first call. The value will be updated by the function call for each subsequent call. If the value is JSON_ID_NONE the end of the array is reached. |
Return value
word | The ID of the array or JSON_ID_NONE if there are no more objects in the array. |
get_string (overloaded)
Gets a string by name.
Parameters
word base | The ID of the base element. |
const char * name | The name of the element. Only used when it's is inside an object. Set to 0 otherwise. |
Return value
const char * | The value of the string or 0 if it's not found. |
get_string (overloaded)
Gets the next array from an array.
Parameters
word base | The ID of the base element. |
word & last | A word reference that must have the value 0 for the first call. The value will be updated by the function call for each subsequent call. If the value is JSON_ID_NONE the end of the array is reached. |
Return value
const char * | The value of the string or 0 if there are no more objects in the array. |
get_int (overloaded)
Gets a 32-bit signed integer by name.
Parameters
word base | The ID of the base element. |
const char * name | The name of the element. Only used when it's is inside an object. Set to 0 otherwise. |
bool * present | If specified, the value will be set to true if the element existed. false otherwise. |
Return value
int | The value of the element or 0 if it's not found. |
get_int (overloaded)
Gets the next 32-bit signed integer from an array.
Parameters
word base | The ID of the base element. |
word & last | A word reference that must have the value 0 for the first call. The value will be updated by the function call for each subsequent call. If the value is JSON_ID_NONE the end of the array is reached. |
bool * present | If specified, the value will be set to true if the element existed. false otherwise. |
Return value
int | The value of the element or 0 if there are no more objects in the array. |
get_unsigned (overloaded)
Gets a 32-bit unsigned integer by name.
Parameters
word base | The ID of the base element. |
const char * name | The name of the element. Only used when it's is inside an object. Set to 0 otherwise. |
bool * present | If specified, the value will be set to true if the element existed. false otherwise. |
Return value
dword | The value of the element or 0 if it's not found. |
get_unsigned (overloaded)
Gets the next 32-bit unsigned integer from an array.
Parameters
word base | The ID of the base element. |
word & last | A word reference that must have the value 0 for the first call. The value will be updated by the function call for each subsequent call. If the value is JSON_ID_NONE the end of the array is reached. |
bool * present | If specified, the value will be set to true if the element existed. false otherwise. |
Return value
dword | The value of the element or 0 if there are no more objects in the array. |
get_long64 (overloaded)
Gets a 64-bit signed integer by name.
Parameters
word base | The ID of the base element. |
const char * name | The name of the element. Only used when it's is inside an object. Set to 0 otherwise. |
bool * present | If specified, the value will be set to true if the element existed. false otherwise. |
Return value
long64 | The value of the element or 0 if it's not found. |
get_long64 (overloaded)
Gets the next 64-bit signed integer from an array.
Parameters
word base | The ID of the base element. |
word & last | A word reference that must have the value 0 for the first call. The value will be updated by the function call for each subsequent call. If the value is JSON_ID_NONE the end of the array is reached. |
bool * present | If specified, the value will be set to true if the element existed. false otherwise. |
Return value
long64 | The value of the element or 0 if there are no more objects in the array. |
get_ulong64 (overloaded)
Gets a 64-bit signed uninteger by name.
Parameters
word base | The ID of the base element. |
const char * name | The name of the element. Only used when it's is inside an object. Set to 0 otherwise. |
bool * present | If specified, the value will be set to true if the element existed. false otherwise. |
Return value
ulong64 | The value of the element or 0 if it's not found. |
get_ulong64 (overloaded)
Gets the next 64-bit unsigned integer from an array.
Parameters
word base | The ID of the base element. |
word & last | A word reference that must have the value 0 for the first call. The value will be updated by the function call for each subsequent call. If the value is JSON_ID_NONE the end of the array is reached. |
bool * present | If specified, the value will be set to true if the element existed. false otherwise. |
Return value
ulong64 | The value of the element or 0 if there are no more objects in the array. |
get_bool (overloaded)
Gets a boolean by name.
Parameters
word base | The ID of the base element. |
const char * name | The name of the element. Only used when it's is inside an object. Set to 0 otherwise. |
bool * present | If specified, the value will be set to true if the element existed. false otherwise. |
Return value
bool | The value of the element or false if it's not found. |
get_bool (overloaded)
Gets the next boolean from an array.
Parameters
word base | The ID of the base element. |
word & last | A word reference that must have the value 0 for the first call. The value will be updated by the function call for each subsequent call. If the value is JSON_ID_NONE the end of the array is reached. |
bool * present | If specified, the value will be set to true if the element existed. false otherwise. |
Return value
bool | The value of the element or false if there are no more objects in the array. |
get_bool_int (overloaded)
Gets a boolean or integer by name.
Parameters
word base | The ID of the base element. |
const char * name | The name of the element. Only used when it's is inside an object. Set to 0 otherwise. |
int & iret | Returns the integer value |
bool * present | If specified, the value will be set to 1 if the element existed and is bool, 2 if the value existed and is integer. Set to 0 otherwise |
Return value
bool | The value of the element or false if it's not found. |
get_double (overloaded)
Gets a double value by name.
Parameters
word base | The ID of the base element. |
const char * name | The name of the element. Only used when it's is inside an object. Set to 0 otherwise. |
bool * present | If specified, the value will be set to true if the element existed. false otherwise. |
Return value
double | The value of the element or 0 if it's not found. |
get_double (overloaded)
Gets the next double value from an array.
Parameters
word base | The ID of the base element. |
word & last | A word reference that must have the value 0 for the first call. The value will be updated by the function call for each subsequent call. If the value is JSON_ID_NONE the end of the array is reached. |
bool * present | If specified, the value will be set to true if the element existed. false otherwise. |
Return value
double | The value of the element or 0 if there are no more objects in the array. |
get_flags (overloaded)
Gets the flags of an given element
Parameters
word handle | The ID of the element. |
Return value
double |
The flags. The only relevant flag is JSON_FLAG_INCOMPLETE. It indicates that the element
using chunked decoding is not yet complete and should not be processed.
|
to_url
Helper function to encodes the structure not as JSON but as URL arguments.
Parameters
word base | The ID of the base element. |
char * b | The output buffer. |
word l | The size of the output buffer. |
const char * prefix | Prefix to be used for the single elements. For elements nested within arrays or objects another prefix of the name of the array/object is added. The prefixes are sepearted by '.' |
bool cont | true indicates that the arguments are to be added to other arguments. This means a '&' is put at the beginning |
Return value
word | The number of bytes that have been written to the output buffer. |
Data types
Defines
Code Examples
The following examples demonstrate the library on the following JSON structure.
{
"name": "John Doe",
"age": 30,
"interests": [
"programming",
"tv series",
"cooking"
],
"email": "john.doe@aol.com"
}
Encoding
char message[256];
json_io json(message);
word base = json.add_object(JSON_ID_ROOT, 0);
json.add_string(base, "name", "John Doe");
json.add_unsigned(base, "age", 30);
word interests = json.add_array(base, "interests");
json.add_string(interests, "programming");
json.add_string(interests, "tv series");
json.add_string(interests, "cooking");
json.add_string(base, "email", "john.doe@aol.com");
json.add_string(interests, "fishing"); // this will have no effect, cause "interests" handle is not valid any more
json.encode();
Decoding
json_io json(message);
json.decode();
word base = json.get_object(JSON_ID_ROOT, 0);
const char * name = json.get_string(base, "name");
dword age = json.get_unsigned(base, "age");
const char * interest_values[4];
word interest_count = 0;
word interests = json.get_array(base, "interests");
word last = 0;
while (last != JSON_ID_NONE && interest_count < 4) {
const char * value = = json.get_string(interests, last);
if (value) {
interest_values[interest_count++] = value;
}
}