mirror of
https://github.com/bblanchon/ArduinoJson.git
synced 2025-09-26 06:50:56 +02:00
Destroyed Bag of Tricks (markdown)
435
Bag-of-Tricks.md
435
Bag-of-Tricks.md
@@ -1,435 +0,0 @@
|
||||
Here are helper class that can help you add missing features.
|
||||
|
||||
## Look for a nested key
|
||||
|
||||
Suppose we've got this json:
|
||||
|
||||
```json
|
||||
{"key1":1,"key2":{"subkey1":3,"subkey2":4}}
|
||||
```
|
||||
|
||||
And we want to check presence of **subkey1** (or **subkey2**). Using **containsKey** method will not work, since **subkey1** and **subkey2** are nested keys inside **key2** value. This function allows to find key presence even if they are nested inside any key value:
|
||||
|
||||
```c++
|
||||
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;
|
||||
}
|
||||
```
|
||||
|
||||
See issue [#322](https://github.com/bblanchon/ArduinoJson/issues/322)
|
||||
|
||||
## Extract an array of integer
|
||||
|
||||
Suppose we have an input like this:
|
||||
|
||||
```json
|
||||
{"leds":[56,60,46]}
|
||||
```
|
||||
|
||||
And we want to get a `int[]` out of it:
|
||||
|
||||
```c++
|
||||
#define MAX_LED_COUNT 10
|
||||
|
||||
int leds[MAX_LED_COUNT];
|
||||
int ledCount = root["leds"].asArray().copyTo(leds);
|
||||
```
|
||||
|
||||
See issue [#246](https://github.com/bblanchon/ArduinoJson/issues/246)
|
||||
|
||||
## Nested array in a nested array
|
||||
|
||||
Imagine you need to generate the following JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"value1": "x",
|
||||
"value2": [
|
||||
[
|
||||
"Yes",
|
||||
"No"
|
||||
],
|
||||
[
|
||||
"Maybe"
|
||||
]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The canonical way to do this is:
|
||||
|
||||
```c++
|
||||
JsonObject& root = jsonBuffer.createObject();
|
||||
|
||||
root["value1"] = "x";
|
||||
JsonArray& value2 = root.createNestedArray("value2");
|
||||
|
||||
JsonArray& yesno = value2.createNestedArray();
|
||||
yesno.add("Yes");
|
||||
yesno.add("No");
|
||||
|
||||
JsonArray& maybe = value2.createNestedArray();
|
||||
maybe.add("Maybe");
|
||||
```
|
||||
|
||||
It's also possible to create the arrays and then put them in the object:
|
||||
|
||||
```c++
|
||||
JsonObject& root = jsonBuffer.createObject();
|
||||
JsonObject& value2 = jsonBuffer.createArray();
|
||||
JsonObject& yesno = jsonBuffer.createArray();
|
||||
JsonObject& maybe = jsonBuffer.createArray();
|
||||
|
||||
root["value1"] = "x";
|
||||
root["value2"] = value2;
|
||||
value2.add(yesno);
|
||||
yesno.add("Yes");
|
||||
yesno.add("No");
|
||||
value2.add(maybe);
|
||||
maybe.add("Maybe");
|
||||
```
|
||||
|
||||
But it's a little less efficient in term of CPU and memory.
|
||||
|
||||
Lastly, it's possible to manually encode the nested array with an undocumented feature:
|
||||
|
||||
```c++
|
||||
JsonObject& root = jsonBuffer.createObject();
|
||||
root["value1"] = "x";
|
||||
root["value2"] = RawJson("[[\"Yes\",\"No\"],[\"Maybe\"]");
|
||||
```
|
||||
|
||||
See issue [#252](https://github.com/bblanchon/ArduinoJson/issues/252)
|
||||
|
||||
## Merging JSON objects
|
||||
|
||||
Suppose we have three JSON objects like these:
|
||||
|
||||
```c++
|
||||
char json1[] = R"({
|
||||
"fingerprint": "1234"
|
||||
})";
|
||||
|
||||
char json2[] = R"({
|
||||
"Trait1":{
|
||||
"someValue": "4321"
|
||||
}
|
||||
})";
|
||||
|
||||
char json3[] = R"({
|
||||
"Trait2":{
|
||||
"anotherValue": "5555"
|
||||
}
|
||||
})";
|
||||
|
||||
JsonObject& jsonObjectRoot = jsonBuffer1.parseObject(json1);
|
||||
JsonObject& jsonObjectOne = jsonBuffer2.parseObject(json2);
|
||||
JsonObject& jsonObjectTwo = jsonBuffer3.parseObject(json3);
|
||||
```
|
||||
And we want a JSON that looks like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"fingerprint": "1234",
|
||||
"SetOfObjects" : {
|
||||
"Trait1":{
|
||||
"someValue": "4321"
|
||||
},
|
||||
"Trait2":{
|
||||
"anotherValue": "5555"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This function allows to merge the JSON objects:
|
||||
|
||||
```c++
|
||||
void merge(JsonObject& dest, JsonObject& src) {
|
||||
for (auto kvp : src) {
|
||||
dest[kvp.key] = kvp.value;
|
||||
}
|
||||
}
|
||||
```
|
||||
Usage:
|
||||
```c++
|
||||
JsonObject& nestedObject = jsonObjectRoot.createNestedObject("SetOfObjects");
|
||||
merge(nestedObject, jsonObjectOne);
|
||||
merge(nestedObject, jsonObjectTwo);
|
||||
```
|
||||
|
||||
See issue [#332](https://github.com/bblanchon/ArduinoJson/issues/332)
|
||||
|
||||
## Buffered output
|
||||
|
||||
Here is a proxy that will put bytes in a buffer before actually writing them to the destination:
|
||||
|
||||
```c++
|
||||
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:
|
||||
|
||||
```c++
|
||||
BufferedPrint<256> bufferedPrint(Serial)
|
||||
root.printTo(bufferedPrint);
|
||||
```
|
||||
|
||||
See issue [#166](https://github.com/bblanchon/ArduinoJson/issues/166).
|
||||
|
||||
## Chunked output
|
||||
|
||||
Here is a proxy that allow to get only part of the output:
|
||||
|
||||
```c++
|
||||
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:
|
||||
|
||||
```c++
|
||||
// print only range [10,20[
|
||||
ChunkPrint chunkPrint(Serial,10,20);
|
||||
root.printTo(chunkPrint);
|
||||
```
|
||||
|
||||
See issue [#206](https://github.com/bblanchon/ArduinoJson/issues/206).
|
||||
|
||||
## Render to ESP8266 WiFiClient
|
||||
|
||||
When used on [ESP8266 Arduino](https://github.com/esp8266/Arduino), there is currently not a direct way to render the JSON to WiFiClient, since it does not implement required Print interface.
|
||||
This utility class serves as an adapter between `Print` and `WiFiClient` classes. Internal buffer is required, otherwise rendering will take several seconds (every byte will be sent as a separate packet). Increase buffer for faster send, if you have spare memory.
|
||||
|
||||
Create this as separate header file `WiFiClientPrint.h` or just include class in your program.
|
||||
|
||||
_Note: You need to call either `flush()` or `stop()` manually after printTo._
|
||||
|
||||
```c++
|
||||
#pragma once
|
||||
|
||||
#include <WiFiClient.h>
|
||||
#include <Print.h>
|
||||
|
||||
template<size_t BUFFER_SIZE = 32>
|
||||
class WiFiClientPrint : public Print
|
||||
{
|
||||
public:
|
||||
WiFiClientPrint(WiFiClient client)
|
||||
: _client(client),
|
||||
_length(0)
|
||||
{
|
||||
}
|
||||
|
||||
~WiFiClientPrint()
|
||||
{
|
||||
#ifdef DEBUG_ESP_PORT
|
||||
// Note: This is manual expansion of assertion macro
|
||||
if (_length != 0) {
|
||||
DEBUG_ESP_PORT.printf("\nassertion failed at " __FILE__ ":%d: " "_length == 0" "\n", __LINE__);
|
||||
// Note: abort() causes stack dump and restart of the ESP
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual size_t write(uint8_t c) override
|
||||
{
|
||||
_buffer[_length++] = c;
|
||||
if (_length == BUFFER_SIZE) {
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
||||
void flush()
|
||||
{
|
||||
if (_length != 0) {
|
||||
_client.write((const uint8_t*)_buffer, _length);
|
||||
_length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
flush();
|
||||
_client.stop();
|
||||
}
|
||||
|
||||
private:
|
||||
WiFiClient _client;
|
||||
uint8_t _buffer[BUFFER_SIZE];
|
||||
size_t _length;
|
||||
};
|
||||
```
|
||||
|
||||
Usage (typically inside `ESP8266WebServer` handler):
|
||||
```c++
|
||||
// #include "WiFiClientPrint.h"
|
||||
|
||||
// ESP8266WebServer _server;
|
||||
// JsonObject json;
|
||||
_server.setContentLength(json.measureLength());
|
||||
_server.send(200, "application/json", "");
|
||||
|
||||
WiFiClientPrint<> p(_server.client());
|
||||
json.printTo(p);
|
||||
p.stop(); // Calls p.flush() and WifiClient.stop()
|
||||
```
|
||||
|
||||
See issue [#166](https://github.com/bblanchon/ArduinoJson/issues/166).
|
||||
|
||||
## Compute hash of JSON output
|
||||
|
||||
Here is how you can compute the CRC32 hash of the JSON output without consuming a lot of memory.
|
||||
|
||||
This can be very handy to compare two JSON trees.
|
||||
|
||||
```c++
|
||||
#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:
|
||||
|
||||
```c++
|
||||
HashPrint hashPrint;
|
||||
root.printTo(hashPrint);
|
||||
Serial.println(hashPrint.hash());
|
||||
```
|
||||
|
||||
See issue [#390](https://github.com/bblanchon/ArduinoJson/issues/390).
|
||||
|
||||
|
||||
## Throw exception when JsonBuffer is too small
|
||||
|
||||
Here is a static decorator that will behave as the decorated `JsonBuffer`, except that it will throw an exception if the allocation fails:
|
||||
|
||||
```c++
|
||||
#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");
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
To use this in your code:
|
||||
|
||||
```c++
|
||||
Throwing<StaticJsonBuffer<200> > jsonBuffer;
|
||||
// or
|
||||
Throwing<DynamicJsonBuffer> jsonBuffer;
|
||||
```
|
||||
|
||||
See issue [#205](https://github.com/bblanchon/ArduinoJson/issues/205).
|
||||
|
||||
## Clone an object or an array
|
||||
|
||||
```c++
|
||||
JsonVariant clone(JsonBuffer& jb, JsonVariant prototype)
|
||||
{
|
||||
if (prototype.is<JsonObject>()) {
|
||||
const JsonObject& protoObj = prototype;
|
||||
JsonObject& newObj = jb.createObject();
|
||||
for (const auto& kvp : protoObj) {
|
||||
newObj[clone(jb, 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<char*>());
|
||||
}
|
||||
|
||||
return prototype;
|
||||
}
|
||||
```
|
||||
|
||||
See issue [#533](https://github.com/bblanchon/ArduinoJson/issues/533)
|
Reference in New Issue
Block a user