Bag of Tricks
This page contains some useful techniques to accomplish unusual things with ArduinoJson.
Look for a nested key
Suppose you have the following JSON document:
{
"key1": 1,
"key2": {
"subkey1": 3,
"subkey2": 4
}
}
And suppose that you want to check the presence of subkey1
.
JsonObject::containsKey()
will not work, since subkey1
is nested inside key2
.
The following function find a key even if is nested:
bool containsNestedKey(const JsonObject& obj, const char* key) {
for (const JsonPair& pair : obj) {
if (!strcmp(pair.key, key))
return true;
if (containsNestedKey(pair.value.as<JsonObject>(), key))
return true;
}
return false;
}
Extract an array of integer
Suppose you have an input like this:
{ "leds": [56, 60, 46] }
And support that you want to get an int[]
out of it.
Here is the simplest way to do that:
#define MAX_LED_COUNT 10
int leds[MAX_LED_COUNT];
int ledCount = root["leds"].as<JsonArray>().copyTo(leds);
Merging JSON objects
Suppose you have two JSON objects:
{
"name": "Benoit"
}
and
{
"age": 36
}
And suppose that your goal is to merge the two objects into one:
{
"name": "Benoit",
"age": 36
}
Well, you can do that easily with the following function:
void merge(JsonObject& dest, const JsonObject& src) {
for (auto kvp : src) {
dest[kvp.key] = kvp.value;
}
}
Buffered output
Here is a proxy that will put bytes in a buffer before actually writing them to the destination:
template <size_t CAPACITY>
class BufferedPrint : public Print {
public:
BufferedPrint(Print& destination) : _destination(destination), _size(0) {}
~BufferedPrint() { flush(); }
virtual size_t write(uint8_t c) {
_buffer[_size++] = c;
if (_size + 1 == CAPACITY) {
flush();
}
}
void flush() {
_buffer[_size] = '\0';
_destination.print(_buffer);
_size = 0;
}
private:
Print& _destination;
size_t _size;
char _buffer[CAPACITY];
};
To use this in your code:
BufferedPrint<256> bufferedPrint(Serial)
root.printTo(bufferedPrint);
Use this class if the Print
implementation sends bytes one-by-one, it will greatly improve the performance of your program.
Chunked output
Here is a proxy that allow to get only part of the output:
class ChunkPrint : public Print {
public:
ChunkPrint(Print& destination, size_t from, size_t to)
: _destination(destination), _to_skip(from), _to_write(to - from) {}
virtual size_t write(uint8_t c) {
if (_to_skip > 0) {
_to_skip--;
} else if (_to_write > 0) {
_to_write--;
return _destination.write(c);
}
return 0;
}
private:
Print& _destination;
size_t _to_skip;
size_t _to_write;
};
To use this in your code:
// print only range [10,20[
ChunkPrint chunkPrint(Serial,10,20);
root.printTo(chunkPrint);
Use this class when you need to send the JSON document in chunks, i.e., not all at once.
Compute hash of JSON output
Here is how you can compute the CRC32 hash of the JSON document without consuming a lot of memory.
#include <FastCRC.h> // https://github.com/FrankBoesing/FastCRC
class HashPrint : public Print {
public:
HashPrint() {
_hash = _hasher.crc32(NULL, 0);
}
virtual size_t write(uint8_t c) {
_hash = _hasher.crc32_upd(&c, 1);
}
uint32_t hash() const {
return _hash;
}
private:
FastCRC32 _hasher;
uint32_t _hash;
};
To use this in your code:
HashPrint hashPrint;
root.printTo(hashPrint);
Serial.println(hashPrint.hash());
Use this class when you want to compare two JSON documents
Throw exception when JsonBuffer is too small
Here is a class that behaves as a JsonBuffer
, except that it will throw an exception if the allocation fails:
#include <stdexcept>
template <typename TJsonBuffer>
struct Throwing : TJsonBuffer {
virtual void *alloc(size_t bytes) {
void *ptr = TJsonBuffer::alloc(bytes);
if (ptr)
return ptr;
else
throw std::runtime_error("allocation failed");
}
};
This class implement the static decorator pattern, and therefore must be used as below:
Throwing<StaticJsonBuffer<200> > jsonBuffer;
// or
Throwing<DynamicJsonBuffer> jsonBuffer;
Clone an object or an array
Here is a function that makes a deep copy of a JsonVariant
:
JsonVariant clone(JsonBuffer& jb, JsonVariant prototype)
{
if (prototype.is<JsonObject>()) {
const JsonObject& protoObj = prototype;
JsonObject& newObj = jb.createObject();
for (const auto& kvp : protoObj) {
newObj[jb.strdup(kvp.key)] = clone(jb, kvp.value);
}
return newObj;
}
if (prototype.is<JsonArray>()) {
const JsonArray& protoArr = prototype;
JsonArray& newArr = jb.createArray();
for (const auto& elem : protoArr) {
newArr.add(clone(jb, elem));
}
return newArr;
}
if (prototype.is<char*>()) {
return jb.strdup(prototype.as<const char*>());
}
return prototype;
}
This function works with JsonObject
and JsonArray
.