Destroyed Bag of Tricks (markdown)

Benoît Blanchon
2017-12-15 15:52:20 +01:00
parent 5f8d83e570
commit ca2185aa2e

@@ -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)