JsonVariant automatically promotes to JsonObject or JsonArray on write

This commit is contained in:
Benoit Blanchon
2019-01-29 14:09:09 +01:00
parent 5aea1363cc
commit 6f55d1e58f
53 changed files with 1197 additions and 541 deletions

View File

@ -22,6 +22,12 @@ HEAD
* Added `JsonDocument::operator[]` * Added `JsonDocument::operator[]`
* Added `ARDUINOJSON_TAB` to configure the indentation character * Added `ARDUINOJSON_TAB` to configure the indentation character
* Reduced the size of the pretty JSON serializer * Reduced the size of the pretty JSON serializer
* Added `add()`, `createNestedArray()` and `createNestedObject()` to `JsonVariant`
* `JsonVariant` automatically promotes to `JsonObject` or `JsonArray` on write.
Calling `JsonVariant::to<T>()` is not required anymore.
* `JsonDocument` now support the same operations as `JsonVariant`.
Calling `JsonDocument::as<T>()` is not required anymore.
* Fixed example `JsonHttpClient.ino`
> ### BREAKING CHANGES > ### BREAKING CHANGES
> >

View File

@ -15,7 +15,11 @@
#include <SD.h> #include <SD.h>
#include <SPI.h> #include <SPI.h>
// Configuration that we'll store on disk // Our configuration structure.
//
// Never use a JsonDocument to store the configuration!
// A JsonDocument is *not* a permanent storage; it's only a temporary storage
// used during the serialization phase.
struct Config { struct Config {
char hostname[64]; char hostname[64];
int port; int port;
@ -29,9 +33,9 @@ void loadConfiguration(const char *filename, Config &config) {
// Open file for reading // Open file for reading
File file = SD.open(filename); File file = SD.open(filename);
// Allocate the document on the stack. // Allocate a temporary JsonDocument
// Don't forget to change the capacity to match your requirements. // Don't forget to change the capacity to match your requirements.
// Use arduinojson.org/assistant to compute the capacity. // Use arduinojson.org/v6/assistant to compute the capacity.
StaticJsonDocument<512> doc; StaticJsonDocument<512> doc;
// Deserialize the JSON document // Deserialize the JSON document
@ -39,16 +43,13 @@ void loadConfiguration(const char *filename, Config &config) {
if (error) if (error)
Serial.println(F("Failed to read file, using default configuration")); Serial.println(F("Failed to read file, using default configuration"));
// Get the root object in the document // Copy values from the JsonDocument to the Config
JsonObject root = doc.as<JsonObject>(); config.port = doc["port"] | 2731;
// Copy values from the JsonObject to the Config
config.port = root["port"] | 2731;
strlcpy(config.hostname, // <- destination strlcpy(config.hostname, // <- destination
root["hostname"] | "example.com", // <- source doc["hostname"] | "example.com", // <- source
sizeof(config.hostname)); // <- destination's capacity sizeof(config.hostname)); // <- destination's capacity
// Close the file (File's destructor doesn't close the file) // Close the file (Curiously, File's destructor doesn't close the file)
file.close(); file.close();
} }
@ -64,24 +65,21 @@ void saveConfiguration(const char *filename, const Config &config) {
return; return;
} }
// Allocate the document on the stack. // Allocate a temporary JsonDocument
// Don't forget to change the capacity to match your requirements. // Don't forget to change the capacity to match your requirements.
// Use arduinojson.org/assistant to compute the capacity. // Use arduinojson.org/assistant to compute the capacity.
StaticJsonDocument<256> doc; StaticJsonDocument<256> doc;
// Make our document contain an object // Set the values in the document
JsonObject root = doc.to<JsonObject>(); doc["hostname"] = config.hostname;
doc["port"] = config.port;
// Set the values in the object
root["hostname"] = config.hostname;
root["port"] = config.port;
// Serialize JSON to file // Serialize JSON to file
if (serializeJson(doc, file) == 0) { if (serializeJson(doc, file) == 0) {
Serial.println(F("Failed to write to file")); Serial.println(F("Failed to write to file"));
} }
// Close the file (File's destructor doesn't close the file) // Close the file
file.close(); file.close();
} }
@ -100,7 +98,7 @@ void printFile(const char *filename) {
} }
Serial.println(); Serial.println();
// Close the file (File's destructor doesn't close the file) // Close the file
file.close(); file.close();
} }

View File

@ -15,7 +15,7 @@ void setup() {
// //
// Inside the brackets, 200 is the RAM allocated to this document. // Inside the brackets, 200 is the RAM allocated to this document.
// Don't forget to change this value to match your requirement. // Don't forget to change this value to match your requirement.
// Use arduinojson.org/assistant to compute the capacity. // Use arduinojson.org/v6/assistant to compute the capacity.
StaticJsonDocument<200> doc; StaticJsonDocument<200> doc;
// StaticJsonObject allocates memory on the stack, it can be // StaticJsonObject allocates memory on the stack, it can be
@ -23,30 +23,30 @@ void setup() {
// //
// DynamicJsonDocument doc(200); // DynamicJsonDocument doc(200);
// Make our document be an object // Add values in the document
JsonObject root = doc.to<JsonObject>();
// Add values in the object
// //
// Most of the time, you can rely on the implicit casts. doc["sensor"] = "gps";
// In other case, you can do root.set<long>("time", 1351824120); doc["time"] = 1351824120;
root["sensor"] = "gps";
root["time"] = 1351824120;
// Add an array. // Add an array.
// //
JsonArray data = root.createNestedArray("data"); JsonArray data = doc.createNestedArray("data");
data.add(48.756080); data.add(48.756080);
data.add(2.302038); data.add(2.302038);
// Generate the minified JSON and send it to the Serial port.
//
serializeJson(doc, Serial); serializeJson(doc, Serial);
// This prints: // The above line prints:
// {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]} // {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]}
// Start a new line
Serial.println(); Serial.println();
// Generate the prettified JSON and send it to the Serial port.
//
serializeJsonPretty(doc, Serial); serializeJsonPretty(doc, Serial);
// This prints: // The above line prints:
// { // {
// "sensor": "gps", // "sensor": "gps",
// "time": 1351824120, // "time": 1351824120,

View File

@ -71,7 +71,7 @@ void setup() {
} }
// Allocate the JSON document // Allocate the JSON document
// Use arduinojson.org/assistant to compute the capacity. // Use arduinojson.org/v6/assistant to compute the capacity.
const size_t capacity = JSON_OBJECT_SIZE(3) + JSON_ARRAY_SIZE(2) + 60; const size_t capacity = JSON_OBJECT_SIZE(3) + JSON_ARRAY_SIZE(2) + 60;
DynamicJsonDocument doc(capacity); DynamicJsonDocument doc(capacity);
@ -84,12 +84,11 @@ void setup() {
} }
// Extract values // Extract values
JsonObject root = doc.as<JsonObject>();
Serial.println(F("Response:")); Serial.println(F("Response:"));
Serial.println(root["sensor"].as<char*>()); Serial.println(doc["sensor"].as<char*>());
Serial.println(root["time"].as<char*>()); Serial.println(doc["time"].as<long>());
Serial.println(root["data"][0].as<char*>()); Serial.println(doc["data"][0].as<float>(), 6);
Serial.println(root["data"][1].as<char*>()); Serial.println(doc["data"][1].as<float>(), 6);
// Disconnect // Disconnect
client.stop(); client.stop();

View File

@ -13,9 +13,9 @@ void setup() {
// Allocate the JSON document // Allocate the JSON document
// //
// Inside the brackets, 200 is the size of the memory pool in bytes. // Inside the brackets, 200 is the capacity of the memory pool in bytes.
// Don't forget to change this value to match your JSON document. // Don't forget to change this value to match your JSON document.
// Use arduinojson.org/assistant to compute the capacity. // Use arduinojson.org/v6/assistant to compute the capacity.
StaticJsonDocument<200> doc; StaticJsonDocument<200> doc;
// StaticJsonDocument<N> allocates memory on the stack, it can be // StaticJsonDocument<N> allocates memory on the stack, it can be
@ -25,9 +25,12 @@ void setup() {
// JSON input string. // JSON input string.
// //
// It's better to use a char[] as shown here. // Using a char[], as shown here, enables the "zero-copy" mode. This mode uses
// If you use a const char* or a String, ArduinoJson will // the minimal amount of memory because the JsonDocument stores pointers to
// have to make a copy of the input in the JsonBuffer. // the input buffer.
// If you use another type of input, ArduinoJson must copy the strings from
// the input to the JsonDocument, so you need to increase the capacity of the
// JsonDocument.
char json[] = char json[] =
"{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}"; "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}";
@ -41,17 +44,14 @@ void setup() {
return; return;
} }
// Get the root object in the document
JsonObject root = doc.as<JsonObject>();
// Fetch values. // Fetch values.
// //
// Most of the time, you can rely on the implicit casts. // Most of the time, you can rely on the implicit casts.
// In other case, you can do root["time"].as<long>(); // In other case, you can do doc["time"].as<long>();
const char* sensor = root["sensor"]; const char* sensor = doc["sensor"];
long time = root["time"]; long time = doc["time"];
double latitude = root["data"][0]; double latitude = doc["data"][0];
double longitude = root["data"][1]; double longitude = doc["data"][1];
// Print values. // Print values.
Serial.println(sensor); Serial.println(sensor);

View File

@ -2,15 +2,15 @@
// Copyright Benoit Blanchon 2014-2018 // Copyright Benoit Blanchon 2014-2018
// MIT License // MIT License
// //
// This example shows how to implement an HTTP server that sends JSON document // This example shows how to implement an HTTP server that sends a JSON document
// in the responses. // in the response.
// It uses the Ethernet library but can be easily adapted for Wifi. // It uses the Ethernet library but can be easily adapted for Wifi.
// //
// It sends the value of the analog and digital pins. // The JSON document contains the values of the analog and digital pins.
// The JSON document looks like the following: // It looks like that:
// { // {
// "analog": [ 0, 1, 2, 3, 4, 5 ], // "analog": [0, 76, 123, 158, 192, 205],
// "digital": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 ] // "digital": [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0]
// } // }
#include <ArduinoJson.h> #include <ArduinoJson.h>
@ -51,15 +51,12 @@ void loop() {
// Read the request (we ignore the content in this example) // Read the request (we ignore the content in this example)
while (client.available()) client.read(); while (client.available()) client.read();
// Allocate the JSON document // Allocate a temporary JsonDocument
// Use arduinojson.org/assistant to compute the capacity. // Use arduinojson.org/v6/assistant to compute the capacity.
StaticJsonDocument<500> doc; StaticJsonDocument<500> doc;
// Make our document represent an object
JsonObject root = doc.to<JsonObject>();
// Create the "analog" array // Create the "analog" array
JsonArray analogValues = root.createNestedArray("analog"); JsonArray analogValues = doc.createNestedArray("analog");
for (int pin = 0; pin < 6; pin++) { for (int pin = 0; pin < 6; pin++) {
// Read the analog input // Read the analog input
int value = analogRead(pin); int value = analogRead(pin);
@ -69,7 +66,7 @@ void loop() {
} }
// Create the "digital" array // Create the "digital" array
JsonArray digitalValues = root.createNestedArray("digital"); JsonArray digitalValues = doc.createNestedArray("digital");
for (int pin = 0; pin < 14; pin++) { for (int pin = 0; pin < 14; pin++) {
// Read the digital input // Read the digital input
int value = digitalRead(pin); int value = digitalRead(pin);
@ -83,9 +80,11 @@ void loop() {
Serial.println(); Serial.println();
// Write response headers // Write response headers
client.println("HTTP/1.0 200 OK"); client.println(F("HTTP/1.0 200 OK"));
client.println("Content-Type: application/json"); client.println(F("Content-Type: application/json"));
client.println("Connection: close"); client.println(F("Connection: close"));
client.print(F("Content-Length: "));
client.println(measureJsonPretty(doc));
client.println(); client.println();
// Write JSON document // Write JSON document

View File

@ -5,10 +5,10 @@
// This example shows how to send a JSON document to a UDP socket. // This example shows how to send a JSON document to a UDP socket.
// At regular interval, it sends a UDP packet that contains the status of // At regular interval, it sends a UDP packet that contains the status of
// analog and digital pins. // analog and digital pins.
// The JSON document looks like the following: // It looks like that:
// { // {
// "analog": [ 0, 1, 2, 3, 4, 5 ], // "analog": [0, 76, 123, 158, 192, 205],
// "digital": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 ] // "digital": [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0]
// } // }
// //
// If you want to test this program, you need to be able to receive the UDP // If you want to test this program, you need to be able to receive the UDP
@ -43,15 +43,12 @@ void setup() {
} }
void loop() { void loop() {
// Allocate the JSON document // Allocate a temporary JsonDocument
// Use arduinojson.org/assistant to compute the capacity. // Use arduinojson.org/v6/assistant to compute the capacity.
StaticJsonDocument<500> doc; StaticJsonDocument<500> doc;
// Make our document represent an object
JsonObject root = doc.to<JsonObject>();
// Create the "analog" array // Create the "analog" array
JsonArray analogValues = root.createNestedArray("analog"); JsonArray analogValues = doc.createNestedArray("analog");
for (int pin = 0; pin < 6; pin++) { for (int pin = 0; pin < 6; pin++) {
// Read the analog input // Read the analog input
int value = analogRead(pin); int value = analogRead(pin);
@ -61,7 +58,7 @@ void loop() {
} }
// Create the "digital" array // Create the "digital" array
JsonArray digitalValues = root.createNestedArray("digital"); JsonArray digitalValues = doc.createNestedArray("digital");
for (int pin = 0; pin < 14; pin++) { for (int pin = 0; pin < 14; pin++) {
// Read the digital input // Read the digital input
int value = digitalRead(pin); int value = digitalRead(pin);

View File

@ -14,9 +14,9 @@ void setup() {
// Allocate the JSON document // Allocate the JSON document
// //
// Inside the brackets, 200 is the size of the memory pool in bytes. // Inside the brackets, 200 is the capacity of the memory pool in bytes.
// Don't forget to change this value to match your JSON document. // Don't forget to change this value to match your JSON document.
// Use arduinojson.org/assistant to compute the capacity. // Use arduinojson.org/v6/assistant to compute the capacity.
StaticJsonDocument<200> doc; StaticJsonDocument<200> doc;
// StaticJsonObject allocates memory on the stack, it can be // StaticJsonObject allocates memory on the stack, it can be
@ -26,9 +26,12 @@ void setup() {
// MessagePack input string. // MessagePack input string.
// //
// It's better to use a char[] as shown here. // Using a char[], as shown here, enables the "zero-copy" mode. This mode uses
// If you use a const char* or a String, ArduinoJson will // the minimal amount of memory because the JsonDocument stores pointers to
// have to make a copy of the input in the JsonBuffer. // the input buffer.
// If you use another type of input, ArduinoJson must copy the strings from
// the input to the JsonDocument, so you need to increase the capacity of the
// JsonDocument.
uint8_t input[] = {131, 166, 115, 101, 110, 115, 111, 114, 163, 103, 112, 115, uint8_t input[] = {131, 166, 115, 101, 110, 115, 111, 114, 163, 103, 112, 115,
164, 116, 105, 109, 101, 206, 80, 147, 50, 248, 164, 100, 164, 116, 105, 109, 101, 206, 80, 147, 50, 248, 164, 100,
97, 116, 97, 146, 203, 64, 72, 96, 199, 58, 188, 148, 97, 116, 97, 146, 203, 64, 72, 96, 199, 58, 188, 148,
@ -40,31 +43,23 @@ void setup() {
// "data": [48.75608, 2.302038] // "data": [48.75608, 2.302038]
// } // }
// doc of the object tree.
//
// It's a reference to the JsonObject, the actual bytes are inside the
// JsonBuffer with all the other nodes of the object tree.
// Memory is freed when jsonBuffer goes out of scope.
DeserializationError error = deserializeMsgPack(doc, input); DeserializationError error = deserializeMsgPack(doc, input);
// Test if parsing succeeds. // Test if parsing succeeded.
if (error) { if (error) {
Serial.print("deserializeMsgPack() failed: "); Serial.print("deserializeMsgPack() failed: ");
Serial.println(error.c_str()); Serial.println(error.c_str());
return; return;
} }
// Get the root object in the document
JsonObject root = doc.as<JsonObject>();
// Fetch values. // Fetch values.
// //
// Most of the time, you can rely on the implicit casts. // Most of the time, you can rely on the implicit casts.
// In other case, you can do root["time"].as<long>(); // In other case, you can do doc["time"].as<long>();
const char* sensor = root["sensor"]; const char* sensor = doc["sensor"];
long time = root["time"]; long time = doc["time"];
double latitude = root["data"][0]; double latitude = doc["data"][0];
double longitude = root["data"][1]; double longitude = doc["data"][1];
// Print values. // Print values.
Serial.println(sensor); Serial.println(sensor);

View File

@ -6,7 +6,7 @@
// ArduinoJson. // ArduinoJson.
// //
// Use Flash strings sparingly, because ArduinoJson duplicates them in the // Use Flash strings sparingly, because ArduinoJson duplicates them in the
// JsonBuffer. Prefer plain old char*, as they are more efficient in term of // JsonDocument. Prefer plain old char*, as they are more efficient in term of
// code size, speed, and memory usage. // code size, speed, and memory usage.
#include <ArduinoJson.h> #include <ArduinoJson.h>
@ -17,8 +17,7 @@ void setup() {
DynamicJsonDocument doc(1024); DynamicJsonDocument doc(1024);
// You can use a Flash String as your JSON input. // You can use a Flash String as your JSON input.
// WARNING: the content of the Flash String will be duplicated in the // WARNING: the string in the input will be duplicated in the JsonDocument.
// JsonBuffer.
deserializeJson(doc, F("{\"sensor\":\"gps\",\"time\":1351824120," deserializeJson(doc, F("{\"sensor\":\"gps\",\"time\":1351824120,"
"\"data\":[48.756080,2.302038]}")); "\"data\":[48.756080,2.302038]}"));
JsonObject obj = doc.as<JsonObject>(); JsonObject obj = doc.as<JsonObject>();
@ -29,12 +28,12 @@ void setup() {
// You can use a Flash String to set an element of a JsonObject // You can use a Flash String to set an element of a JsonObject
// WARNING: the content of the Flash String will be duplicated in the // WARNING: the content of the Flash String will be duplicated in the
// JsonBuffer. // JsonDocument.
obj[F("time")] = time; obj[F("time")] = time;
// You can set a Flash String to a JsonObject or JsonArray: // You can set a Flash String to a JsonObject or JsonArray:
// WARNING: the content of the Flash String will be duplicated in the // WARNING: the content of the Flash String will be duplicated in the
// JsonBuffer. // JsonDocument.
obj["sensor"] = F("gps"); obj["sensor"] = F("gps");
// It works with serialized() too: // It works with serialized() too:

View File

@ -5,7 +5,7 @@
// This example shows the different ways you can use String with ArduinoJson. // This example shows the different ways you can use String with ArduinoJson.
// //
// Use String objects sparingly, because ArduinoJson duplicates them in the // Use String objects sparingly, because ArduinoJson duplicates them in the
// JsonBuffer. Prefer plain old char[], as they are more efficient in term of // JsonDocument. Prefer plain old char[], as they are more efficient in term of
// code size, speed, and memory usage. // code size, speed, and memory usage.
#include <ArduinoJson.h> #include <ArduinoJson.h>
@ -14,7 +14,7 @@ void setup() {
DynamicJsonDocument doc(1024); DynamicJsonDocument doc(1024);
// You can use a String as your JSON input. // You can use a String as your JSON input.
// WARNING: the content of the String will be duplicated in the JsonBuffer. // WARNING: the string in the input will be duplicated in the JsonDocument.
String input = String input =
"{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}"; "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}";
deserializeJson(doc, input); deserializeJson(doc, input);
@ -25,11 +25,11 @@ void setup() {
long time = obj[String("time")]; long time = obj[String("time")];
// You can use a String to set an element of a JsonObject // You can use a String to set an element of a JsonObject
// WARNING: the content of the String will be duplicated in the JsonBuffer. // WARNING: the content of the String will be duplicated in the JsonDocument.
obj[String("time")] = time; obj[String("time")] = time;
// You can get a String from a JsonObject or JsonArray: // You can get a String from a JsonObject or JsonArray:
// No duplication is done, at least not in the JsonBuffer. // No duplication is done, at least not in the JsonDocument.
String sensor = obj["sensor"]; String sensor = obj["sensor"];
// Unfortunately, the following doesn't work (issue #118): // Unfortunately, the following doesn't work (issue #118):
@ -38,14 +38,14 @@ void setup() {
sensor = obj["sensor"].as<String>(); sensor = obj["sensor"].as<String>();
// You can set a String to a JsonObject or JsonArray: // You can set a String to a JsonObject or JsonArray:
// WARNING: the content of the String will be duplicated in the JsonBuffer. // WARNING: the content of the String will be duplicated in the JsonDocument.
obj["sensor"] = sensor; obj["sensor"] = sensor;
// It works with serialized() too: // It works with serialized() too:
obj["sensor"] = serialized(sensor); obj["sensor"] = serialized(sensor);
// You can also concatenate strings // You can also concatenate strings
// WARNING: the content of the String will be duplicated in the JsonBuffer. // WARNING: the content of the String will be duplicated in the JsonDocument.
obj[String("sen") + "sor"] = String("gp") + "s"; obj[String("sen") + "sor"] = String("gp") + "s";
// You can compare the content of a JsonObject with a String // You can compare the content of a JsonObject with a String

View File

@ -14,10 +14,10 @@
#include "ArduinoJson/Document/StaticJsonDocument.hpp" #include "ArduinoJson/Document/StaticJsonDocument.hpp"
#include "ArduinoJson/Array/ArrayImpl.hpp" #include "ArduinoJson/Array/ArrayImpl.hpp"
#include "ArduinoJson/Array/ArraySubscript.hpp" #include "ArduinoJson/Array/ElementProxy.hpp"
#include "ArduinoJson/Collection/CollectionImpl.hpp" #include "ArduinoJson/Collection/CollectionImpl.hpp"
#include "ArduinoJson/Object/MemberProxy.hpp"
#include "ArduinoJson/Object/ObjectImpl.hpp" #include "ArduinoJson/Object/ObjectImpl.hpp"
#include "ArduinoJson/Object/ObjectSubscript.hpp"
#include "ArduinoJson/Variant/VariantAsImpl.hpp" #include "ArduinoJson/Variant/VariantAsImpl.hpp"
#include "ArduinoJson/Variant/VariantImpl.hpp" #include "ArduinoJson/Variant/VariantImpl.hpp"

View File

@ -9,11 +9,14 @@
namespace ARDUINOJSON_NAMESPACE { namespace ARDUINOJSON_NAMESPACE {
inline ArrayRef ArrayRef::createNestedArray() const { template <typename TArray>
return add().to<ArrayRef>(); inline ArrayRef ArrayShortcuts<TArray>::createNestedArray() const {
return impl()->add().template to<ArrayRef>();
} }
inline ObjectRef ArrayRef::createNestedObject() const { template <typename TArray>
return add().to<ObjectRef>(); inline ObjectRef ArrayShortcuts<TArray>::createNestedObject() const {
return impl()->add().template to<ObjectRef>();
} }
} // namespace ARDUINOJSON_NAMESPACE } // namespace ARDUINOJSON_NAMESPACE

View File

@ -16,11 +16,16 @@
namespace ARDUINOJSON_NAMESPACE { namespace ARDUINOJSON_NAMESPACE {
class ObjectRef; class ObjectRef;
class ArraySubscript; template <typename>
class ElementProxy;
template <typename TData> template <typename TData>
class ArrayRefBase { class ArrayRefBase {
public: public:
operator VariantConstRef() const {
return VariantConstRef(reinterpret_cast<const VariantData*>(_data));
}
template <typename Visitor> template <typename Visitor>
FORCE_INLINE void accept(Visitor& visitor) const { FORCE_INLINE void accept(Visitor& visitor) const {
arrayAccept(_data, visitor); arrayAccept(_data, visitor);
@ -30,10 +35,6 @@ class ArrayRefBase {
return _data == 0; return _data == 0;
} }
FORCE_INLINE VariantConstRef operator[](size_t index) const {
return VariantConstRef(_data ? _data->get(index) : 0);
}
FORCE_INLINE size_t memoryUsage() const { FORCE_INLINE size_t memoryUsage() const {
return _data ? _data->memoryUsage() : 0; return _data ? _data->memoryUsage() : 0;
} }
@ -74,9 +75,15 @@ class ArrayConstRef : public ArrayRefBase<const CollectionData>,
FORCE_INLINE bool operator==(ArrayConstRef rhs) const { FORCE_INLINE bool operator==(ArrayConstRef rhs) const {
return arrayEquals(_data, rhs._data); return arrayEquals(_data, rhs._data);
} }
FORCE_INLINE VariantConstRef operator[](size_t index) const {
return VariantConstRef(_data ? _data->get(index) : 0);
}
}; };
class ArrayRef : public ArrayRefBase<CollectionData>, public Visitable { class ArrayRef : public ArrayRefBase<CollectionData>,
public ArrayShortcuts<ArrayRef>,
public Visitable {
typedef ArrayRefBase<CollectionData> base_type; typedef ArrayRefBase<CollectionData> base_type;
public: public:
@ -94,27 +101,7 @@ class ArrayRef : public ArrayRefBase<CollectionData>, public Visitable {
return ArrayConstRef(_data); return ArrayConstRef(_data);
} }
// Adds the specified value at the end of the array. using ArrayShortcuts::add;
//
// bool add(TValue);
// TValue = bool, long, int, short, float, double, serialized, VariantRef,
// std::string, String, ObjectRef
template <typename T>
FORCE_INLINE bool add(const T& value) const {
return add().set(value);
}
// Adds the specified value at the end of the array.
FORCE_INLINE bool add(ArrayConstRef value) const {
return add().set(value);
}
//
// bool add(TValue);
// TValue = char*, const char*, const __FlashStringHelper*
template <typename T>
FORCE_INLINE bool add(T* value) const {
return add().set(value);
}
VariantRef add() const { VariantRef add() const {
return VariantRef(_pool, arrayAdd(_data, _pool)); return VariantRef(_pool, arrayAdd(_data, _pool));
} }
@ -187,11 +174,6 @@ class ArrayRef : public ArrayRefBase<CollectionData>, public Visitable {
} }
} }
FORCE_INLINE ArrayRef createNestedArray() const;
FORCE_INLINE ObjectRef createNestedObject() const;
FORCE_INLINE ArraySubscript operator[](size_t index) const;
FORCE_INLINE bool operator==(ArrayRef rhs) const { FORCE_INLINE bool operator==(ArrayRef rhs) const {
return arrayEquals(_data, rhs._data); return arrayEquals(_data, rhs._data);
} }

View File

@ -0,0 +1,47 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#pragma once
#include "../Polyfills/attributes.hpp"
#include "../Polyfills/type_traits.hpp"
namespace ARDUINOJSON_NAMESPACE {
// Forward declarations.
template <typename>
class ElementProxy;
template <typename TArray>
class ArrayShortcuts {
public:
// Returns the element at specified index if the variant is an array.
FORCE_INLINE ElementProxy<const TArray &> operator[](size_t index) const;
FORCE_INLINE ObjectRef createNestedObject() const;
FORCE_INLINE ArrayRef createNestedArray() const;
// Adds the specified value at the end of the array.
//
// bool add(TValue);
// TValue = bool, long, int, short, float, double, serialized, VariantRef,
// std::string, String, ObjectRef
template <typename T>
FORCE_INLINE bool add(const T &value) const {
return impl()->add().set(value);
}
//
// bool add(TValue);
// TValue = char*, const char*, const __FlashStringHelper*
template <typename T>
FORCE_INLINE bool add(T *value) const {
return impl()->add().set(value);
}
private:
const TArray *impl() const {
return static_cast<const TArray *>(this);
}
};
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -13,14 +13,18 @@
#endif #endif
namespace ARDUINOJSON_NAMESPACE { namespace ARDUINOJSON_NAMESPACE {
class ArraySubscript : public VariantOperators<ArraySubscript>,
template <typename TArray>
class ElementProxy : public VariantOperators<ElementProxy<TArray> >,
public Visitable { public Visitable {
typedef ElementProxy<TArray> this_type;
public: public:
FORCE_INLINE ArraySubscript(ArrayRef array, size_t index) FORCE_INLINE ElementProxy(TArray array, size_t index)
: _array(array), _index(index) {} : _array(array), _index(index) {}
FORCE_INLINE ArraySubscript& operator=(const ArraySubscript& src) { FORCE_INLINE this_type& operator=(const this_type& src) {
get_impl().set(src.as<VariantConstRef>()); getElement().set(src.as<VariantConstRef>());
return *this; return *this;
} }
@ -30,36 +34,36 @@ class ArraySubscript : public VariantOperators<ArraySubscript>,
// TValue = bool, long, int, short, float, double, serialized, VariantRef, // TValue = bool, long, int, short, float, double, serialized, VariantRef,
// std::string, String, ArrayRef, ObjectRef // std::string, String, ArrayRef, ObjectRef
template <typename T> template <typename T>
FORCE_INLINE ArraySubscript& operator=(const T& src) { FORCE_INLINE this_type& operator=(const T& src) {
get_impl().set(src); getElement().set(src);
return *this; return *this;
} }
// //
// operator=(TValue) // operator=(TValue)
// TValue = char*, const char*, const __FlashStringHelper* // TValue = char*, const char*, const __FlashStringHelper*
template <typename T> template <typename T>
FORCE_INLINE ArraySubscript& operator=(T* src) { FORCE_INLINE this_type& operator=(T* src) {
get_impl().set(src); getElement().set(src);
return *this; return *this;
} }
FORCE_INLINE bool isNull() const { FORCE_INLINE bool isNull() const {
return get_impl().isNull(); return getElement().isNull();
} }
template <typename T> template <typename T>
FORCE_INLINE typename VariantAs<T>::type as() const { FORCE_INLINE typename VariantAs<T>::type as() const {
return get_impl().as<T>(); return getElement().template as<T>();
} }
template <typename T> template <typename T>
FORCE_INLINE bool is() const { FORCE_INLINE bool is() const {
return get_impl().is<T>(); return getElement().template is<T>();
} }
template <typename T> template <typename T>
FORCE_INLINE typename VariantTo<T>::type to() const { FORCE_INLINE typename VariantTo<T>::type to() const {
return get_impl().to<T>(); return getElement().template to<T>();
} }
// Replaces the value // Replaces the value
@ -69,42 +73,65 @@ class ArraySubscript : public VariantOperators<ArraySubscript>,
// std::string, String, ArrayRef, ObjectRef // std::string, String, ArrayRef, ObjectRef
template <typename TValue> template <typename TValue>
FORCE_INLINE bool set(const TValue& value) const { FORCE_INLINE bool set(const TValue& value) const {
return get_impl().set(value); return getElement().set(value);
} }
// //
// bool set(TValue) // bool set(TValue)
// TValue = char*, const char*, const __FlashStringHelper* // TValue = char*, const char*, const __FlashStringHelper*
template <typename TValue> template <typename TValue>
FORCE_INLINE bool set(TValue* value) const { FORCE_INLINE bool set(TValue* value) const {
return get_impl().set(value); return getElement().set(value);
} }
template <typename Visitor> template <typename Visitor>
void accept(Visitor& visitor) const { void accept(Visitor& visitor) const {
return get_impl().accept(visitor); return getElement().accept(visitor);
} }
FORCE_INLINE size_t size() const { FORCE_INLINE size_t size() const {
return get_impl().size(); return getElement().size();
}
template <typename TNestedKey>
VariantRef get(TNestedKey* key) const {
return getElement().get(key);
}
template <typename TNestedKey>
VariantRef get(const TNestedKey& key) const {
return getElement().get(key);
}
template <typename TNestedKey>
VariantRef getOrCreate(TNestedKey* key) const {
return getElement().getOrCreate(key);
}
template <typename TNestedKey>
VariantRef getOrCreate(const TNestedKey& key) const {
return getElement().getOrCreate(key);
}
using ArrayShortcuts<ElementProxy>::add;
VariantRef add() const {
return getElement().add();
} }
private: private:
FORCE_INLINE VariantRef get_impl() const { FORCE_INLINE VariantRef getElement() const {
return _array.get(_index); return _array.get(_index);
} }
ArrayRef _array; TArray _array;
const size_t _index; const size_t _index;
}; };
template <typename TImpl> template <typename TArray>
inline ArraySubscript VariantSubscripts<TImpl>::operator[](size_t index) const { inline ElementProxy<const TArray&> ArrayShortcuts<TArray>::operator[](
return impl()->template as<ArrayRef>()[index]; size_t index) const {
return ElementProxy<const TArray&>(*impl(), index);
} }
inline ArraySubscript ArrayRef::operator[](size_t index) const {
return ArraySubscript(*this, index);
}
} // namespace ARDUINOJSON_NAMESPACE } // namespace ARDUINOJSON_NAMESPACE
#ifdef _MSC_VER #ifdef _MSC_VER

View File

@ -9,7 +9,8 @@
#include "../Variant/VariantRef.hpp" #include "../Variant/VariantRef.hpp"
#include "../Variant/VariantTo.hpp" #include "../Variant/VariantTo.hpp"
#include "../Array/ArraySubscript.hpp" #include "../Array/ElementProxy.hpp"
#include "../Object/MemberProxy.hpp"
namespace ARDUINOJSON_NAMESPACE { namespace ARDUINOJSON_NAMESPACE {
@ -81,22 +82,51 @@ class JsonDocument : public Visitable {
return _data; return _data;
} }
// ObjectSubscript operator[](TKey) ArrayRef createNestedArray() {
// TKey = const std::string&, const String& return add().to<ArrayRef>();
template <typename TKey>
FORCE_INLINE typename enable_if<IsString<TKey>::value,
ObjectSubscript<const TKey&> >::type
operator[](const TKey& key) {
return getVariant()[key];
} }
// ObjectSubscript operator[](TKey); template <typename TKey>
// TKey = const char*, const char[N], const __FlashStringHelper* ArrayRef createNestedArray(TKey* key) {
return getOrCreate(key).template to<ArrayRef>();
}
template <typename TKey>
ArrayRef createNestedArray(const TKey& key) {
return getOrCreate(key).template to<ArrayRef>();
}
ObjectRef createNestedObject() {
return add().to<ObjectRef>();
}
template <typename TKey>
ObjectRef createNestedObject(TKey* key) {
return getOrCreate(key).template to<ObjectRef>();
}
template <typename TKey>
ObjectRef createNestedObject(const TKey& key) {
return getOrCreate(key).template to<ObjectRef>();
}
// MemberProxy operator[](TKey)
// TKey = const std::string&, const String&
template <typename TKey> template <typename TKey>
FORCE_INLINE FORCE_INLINE
typename enable_if<IsString<TKey*>::value, ObjectSubscript<TKey*> >::type typename enable_if<IsString<TKey>::value,
MemberProxy<JsonDocument&, const TKey&> >::type
operator[](const TKey& key) {
return MemberProxy<JsonDocument&, const TKey&>(*this, key);
}
// MemberProxy operator[](TKey);
// TKey = const char*, const char[N], const __FlashStringHelper*
template <typename TKey>
FORCE_INLINE typename enable_if<IsString<TKey*>::value,
MemberProxy<JsonDocument&, TKey*> >::type
operator[](TKey* key) { operator[](TKey* key) {
return getVariant()[key]; return MemberProxy<JsonDocument&, TKey*>(*this, key);
} }
// VariantConstRef operator[](TKey) const // VariantConstRef operator[](TKey) const
@ -115,12 +145,56 @@ class JsonDocument : public Visitable {
return getVariant()[key]; return getVariant()[key];
} }
FORCE_INLINE ArraySubscript operator[](size_t index) { FORCE_INLINE ElementProxy<JsonDocument&> operator[](size_t index) {
return getVariant()[index]; return ElementProxy<JsonDocument&>(*this, index);
} }
FORCE_INLINE VariantConstRef operator[](size_t index) const { FORCE_INLINE VariantConstRef operator[](size_t index) const {
return getVariant()[index]; return VariantConstRef(_data.get(index));
}
FORCE_INLINE VariantRef get(size_t index) {
return VariantRef(&_pool, _data.get(index));
}
template <typename TKey>
FORCE_INLINE VariantRef get(TKey* key) {
return VariantRef(&_pool, _data.get(wrapString(key)));
}
template <typename TKey>
FORCE_INLINE typename enable_if<IsString<TKey>::value, VariantRef>::type get(
const TKey& key) {
return VariantRef(&_pool, _data.get(wrapString(key)));
}
template <typename TKey>
FORCE_INLINE VariantRef getOrCreate(TKey* key) {
return VariantRef(&_pool, _data.getOrCreate(wrapString(key), &_pool));
}
template <typename TKey>
FORCE_INLINE VariantRef getOrCreate(const TKey& key) {
return VariantRef(&_pool, _data.getOrCreate(wrapString(key), &_pool));
}
FORCE_INLINE VariantRef add() {
return VariantRef(&_pool, _data.add(&_pool));
}
//
// bool add(TValue);
// TValue = bool, long, int, short, float, double, serialized, VariantRef,
// std::string, String, ObjectRef
template <typename T>
FORCE_INLINE bool add(const T& value) {
return add().set(value);
}
//
// bool add(TValue);
// TValue = char*, const char*, const __FlashStringHelper*
template <typename T>
FORCE_INLINE bool add(T* value) {
return add().set(value);
} }
protected: protected:

View File

@ -15,4 +15,7 @@ struct Visitable {
template <typename T> template <typename T>
struct IsVisitable : is_base_of<Visitable, T> {}; struct IsVisitable : is_base_of<Visitable, T> {};
template <typename T>
struct IsVisitable<T&> : IsVisitable<T> {};
} // namespace ARDUINOJSON_NAMESPACE } // namespace ARDUINOJSON_NAMESPACE

View File

@ -15,21 +15,21 @@
namespace ARDUINOJSON_NAMESPACE { namespace ARDUINOJSON_NAMESPACE {
template <typename TStringRef> template <typename TObject, typename TString>
class ObjectSubscript : public VariantOperators<ObjectSubscript<TStringRef> >, class MemberProxy : public VariantOperators<MemberProxy<TObject, TString> >,
public Visitable { public Visitable {
typedef ObjectSubscript<TStringRef> this_type; typedef MemberProxy<TObject, TString> this_type;
public: public:
FORCE_INLINE ObjectSubscript(ObjectRef object, TStringRef key) FORCE_INLINE MemberProxy(TObject variant, TString key)
: _object(object), _key(key) {} : _object(variant), _key(key) {}
operator VariantConstRef() const { FORCE_INLINE operator VariantConstRef() const {
return get_impl(); return getMember();
} }
FORCE_INLINE this_type &operator=(const this_type &src) { FORCE_INLINE this_type &operator=(const this_type &src) {
set_impl().set(src); getOrCreateMember().set(src);
return *this; return *this;
} }
@ -41,7 +41,7 @@ class ObjectSubscript : public VariantOperators<ObjectSubscript<TStringRef> >,
template <typename TValue> template <typename TValue>
FORCE_INLINE typename enable_if<!is_array<TValue>::value, this_type &>::type FORCE_INLINE typename enable_if<!is_array<TValue>::value, this_type &>::type
operator=(const TValue &src) { operator=(const TValue &src) {
set_impl().set(src); getOrCreateMember().set(src);
return *this; return *this;
} }
// //
@ -49,27 +49,27 @@ class ObjectSubscript : public VariantOperators<ObjectSubscript<TStringRef> >,
// TValue = char*, const char*, const __FlashStringHelper* // TValue = char*, const char*, const __FlashStringHelper*
template <typename TValue> template <typename TValue>
FORCE_INLINE this_type &operator=(TValue *src) { FORCE_INLINE this_type &operator=(TValue *src) {
set_impl().set(src); getOrCreateMember().set(src);
return *this; return *this;
} }
FORCE_INLINE bool isNull() const { FORCE_INLINE bool isNull() const {
return get_impl().isNull(); return getMember().isNull();
} }
template <typename TValue> template <typename TValue>
FORCE_INLINE typename VariantAs<TValue>::type as() const { FORCE_INLINE typename VariantAs<TValue>::type as() const {
return get_impl().template as<TValue>(); return getMember().template as<TValue>();
} }
template <typename TValue> template <typename TValue>
FORCE_INLINE bool is() const { FORCE_INLINE bool is() const {
return get_impl().template is<TValue>(); return getMember().template is<TValue>();
} }
template <typename TValue> template <typename TValue>
FORCE_INLINE typename VariantTo<TValue>::type to() { FORCE_INLINE typename VariantTo<TValue>::type to() {
return set_impl().template to<TValue>(); return getOrCreateMember().template to<TValue>();
} }
// Sets the specified value. // Sets the specified value.
@ -81,48 +81,73 @@ class ObjectSubscript : public VariantOperators<ObjectSubscript<TStringRef> >,
template <typename TValue> template <typename TValue>
FORCE_INLINE typename enable_if<!is_array<TValue>::value, bool>::type set( FORCE_INLINE typename enable_if<!is_array<TValue>::value, bool>::type set(
const TValue &value) { const TValue &value) {
return set_impl().set(value); return getOrCreateMember().set(value);
} }
// //
// bool set(TValue); // bool set(TValue);
// TValue = char*, const char, const __FlashStringHelper* // TValue = char*, const char, const __FlashStringHelper*
template <typename TValue> template <typename TValue>
FORCE_INLINE bool set(const TValue *value) { FORCE_INLINE bool set(const TValue *value) {
return set_impl().set(value); return getOrCreateMember().set(value);
} }
template <typename Visitor> template <typename Visitor>
void accept(Visitor &visitor) const { void accept(Visitor &visitor) const {
return get_impl().accept(visitor); return getMember().accept(visitor);
}
using ArrayShortcuts<MemberProxy>::add;
FORCE_INLINE VariantRef add() const {
return getOrCreateMember().add();
}
template <typename TNestedKey>
FORCE_INLINE VariantRef get(TNestedKey *key) const {
return getMember().get(key);
}
template <typename TNestedKey>
FORCE_INLINE VariantRef get(const TNestedKey &key) const {
return getMember().get(key);
}
template <typename TNestedKey>
FORCE_INLINE VariantRef getOrCreate(TNestedKey *key) const {
return getOrCreateMember().getOrCreate(key);
}
template <typename TNestedKey>
FORCE_INLINE VariantRef getOrCreate(const TNestedKey &key) const {
return getOrCreateMember().getOrCreate(key);
} }
private: private:
FORCE_INLINE VariantRef get_impl() const { FORCE_INLINE VariantRef getMember() const {
return _object.get(_key); return _object.get(_key);
} }
FORCE_INLINE VariantRef set_impl() const { FORCE_INLINE VariantRef getOrCreateMember() const {
return _object.set(_key); return _object.getOrCreate(_key);
} }
ObjectRef _object; TObject _object;
TStringRef _key; TString _key;
}; };
template <typename TImpl> template <typename TObject>
template <typename TString> template <typename TString>
inline typename enable_if<IsString<TString>::value, inline typename enable_if<IsString<TString>::value,
ObjectSubscript<const TString &> >::type MemberProxy<const TObject &, const TString &> >::type
VariantSubscripts<TImpl>::operator[](const TString &key) const { ObjectShortcuts<TObject>::operator[](const TString &key) const {
return impl()->template as<ObjectRef>()[key]; return MemberProxy<const TObject &, const TString &>(*impl(), key);
} }
template <typename TImpl> template <typename TObject>
template <typename TString> template <typename TString>
inline typename enable_if<IsString<TString *>::value, inline typename enable_if<IsString<TString *>::value,
ObjectSubscript<TString *> >::type MemberProxy<const TObject &, TString *> >::type
VariantSubscripts<TImpl>::operator[](TString *key) const { ObjectShortcuts<TObject>::operator[](TString *key) const {
return impl()->template as<ObjectRef>()[key]; return MemberProxy<const TObject &, TString *>(*impl(), key);
} }
} // namespace ARDUINOJSON_NAMESPACE } // namespace ARDUINOJSON_NAMESPACE

View File

@ -40,7 +40,8 @@ void objectRemove(CollectionData *obj, TKey key) {
} }
template <typename TKey> template <typename TKey>
inline VariantData *objectSet(CollectionData *obj, TKey key, MemoryPool *pool) { inline VariantData *objectGetOrCreate(CollectionData *obj, TKey key,
MemoryPool *pool) {
if (!obj) return 0; if (!obj) return 0;
// ignore null key // ignore null key

View File

@ -9,13 +9,31 @@
namespace ARDUINOJSON_NAMESPACE { namespace ARDUINOJSON_NAMESPACE {
template <typename TObject>
template <typename TString> template <typename TString>
inline ArrayRef ObjectRef::createNestedArray(const TString& key) const { inline ArrayRef ObjectShortcuts<TObject>::createNestedArray(
return set(key).template to<ArrayRef>(); const TString& key) const {
return impl()->getOrCreate(key).template to<ArrayRef>();
} }
template <typename TObject>
template <typename TString> template <typename TString>
inline ArrayRef ObjectRef::createNestedArray(TString* key) const { inline ArrayRef ObjectShortcuts<TObject>::createNestedArray(
return set(key).template to<ArrayRef>(); TString* key) const {
return impl()->getOrCreate(key).template to<ArrayRef>();
}
template <typename TObject>
template <typename TKey>
ObjectRef ObjectShortcuts<TObject>::createNestedObject(const TKey& key) const {
return impl()->getOrCreate(key).template to<ObjectRef>();
}
//
// ObjectRef createNestedObject(TKey);
// TKey = char*, const char*, char[], const char[], const __FlashStringHelper*
template <typename TObject>
template <typename TKey>
ObjectRef ObjectShortcuts<TObject>::createNestedObject(TKey* key) const {
return impl()->getOrCreate(key).template to<ObjectRef>();
} }
} // namespace ARDUINOJSON_NAMESPACE } // namespace ARDUINOJSON_NAMESPACE

View File

@ -17,6 +17,10 @@ namespace ARDUINOJSON_NAMESPACE {
template <typename TData> template <typename TData>
class ObjectRefBase { class ObjectRefBase {
public: public:
operator VariantConstRef() const {
return VariantConstRef(reinterpret_cast<const VariantData*>(_data));
}
template <typename Visitor> template <typename Visitor>
FORCE_INLINE void accept(Visitor& visitor) const { FORCE_INLINE void accept(Visitor& visitor) const {
objectAccept(_data, visitor); objectAccept(_data, visitor);
@ -127,7 +131,9 @@ class ObjectConstRef : public ObjectRefBase<const CollectionData>,
} }
}; };
class ObjectRef : public ObjectRefBase<CollectionData>, public Visitable { class ObjectRef : public ObjectRefBase<CollectionData>,
public ObjectShortcuts<ObjectRef>,
public Visitable {
typedef ObjectRefBase<CollectionData> base_type; typedef ObjectRefBase<CollectionData> base_type;
public: public:
@ -164,68 +170,30 @@ class ObjectRef : public ObjectRefBase<CollectionData>, public Visitable {
return _data->copyFrom(*src._data, _pool); return _data->copyFrom(*src._data, _pool);
} }
// Creates and adds a ArrayRef.
//
// ArrayRef createNestedArray(TKey);
// TKey = const std::string&, const String&
template <typename TKey>
FORCE_INLINE ArrayRef createNestedArray(const TKey& key) const;
// ArrayRef createNestedArray(TKey);
// TKey = char*, const char*, char[], const char[], const __FlashStringHelper*
template <typename TKey>
FORCE_INLINE ArrayRef createNestedArray(TKey* key) const;
// Creates and adds a ObjectRef.
//
// ObjectRef createNestedObject(TKey);
// TKey = const std::string&, const String&
template <typename TKey>
FORCE_INLINE ObjectRef createNestedObject(const TKey& key) const {
return set(key).template to<ObjectRef>();
}
//
// ObjectRef createNestedObject(TKey);
// TKey = char*, const char*, char[], const char[], const __FlashStringHelper*
template <typename TKey>
FORCE_INLINE ObjectRef createNestedObject(TKey* key) const {
return set(key).template to<ObjectRef>();
}
// Gets the value associated with the specified key. // Gets the value associated with the specified key.
// //
// TValue get<TValue>(TKey) const; // VariantRef get<TValue>(TKey) const;
// TKey = const std::string&, const String& // TKey = const std::string&, const String&
// TValue = bool, char, long, int, short, float, double,
// std::string, String, ArrayRef, ObjectRef
template <typename TKey> template <typename TKey>
FORCE_INLINE VariantRef get(const TKey& key) const { FORCE_INLINE VariantRef get(const TKey& key) const {
return get_impl(wrapString(key)); return get_impl(wrapString(key));
} }
// //
// TValue get<TValue>(TKey) const; // VariantRef get<TValue>(TKey) const;
// TKey = char*, const char*, const __FlashStringHelper* // TKey = char*, const char*, const __FlashStringHelper*
// TValue = bool, char, long, int, short, float, double,
// std::string, String, ArrayRef, ObjectRef
template <typename TKey> template <typename TKey>
FORCE_INLINE VariantRef get(TKey* key) const { FORCE_INLINE VariantRef get(TKey* key) const {
return get_impl(wrapString(key)); return get_impl(wrapString(key));
} }
// Gets or sets the value associated with the specified key.
//
// ObjectSubscript operator[](TKey)
// TKey = const std::string&, const String&
template <typename TKey> template <typename TKey>
FORCE_INLINE ObjectSubscript<const TKey&> operator[](const TKey& key) const { FORCE_INLINE VariantRef getOrCreate(TKey* key) const {
return ObjectSubscript<const TKey&>(*this, key); return getOrCreate_impl(wrapString(key));
} }
//
// ObjectSubscript operator[](TKey)
// TKey = char*, const char*, char[], const char[N], const
// __FlashStringHelper*
template <typename TKey> template <typename TKey>
FORCE_INLINE ObjectSubscript<TKey*> operator[](TKey* key) const { FORCE_INLINE VariantRef getOrCreate(const TKey& key) const {
return ObjectSubscript<TKey*>(*this, key); return getOrCreate_impl(wrapString(key));
} }
FORCE_INLINE bool operator==(ObjectRef rhs) const { FORCE_INLINE bool operator==(ObjectRef rhs) const {
@ -253,16 +221,6 @@ class ObjectRef : public ObjectRefBase<CollectionData>, public Visitable {
objectRemove(_data, wrapString(key)); objectRemove(_data, wrapString(key));
} }
template <typename TKey>
FORCE_INLINE VariantRef set(TKey* key) const {
return set_impl(wrapString(key));
}
template <typename TKey>
FORCE_INLINE VariantRef set(const TKey& key) const {
return set_impl(wrapString(key));
}
private: private:
template <typename TKey> template <typename TKey>
FORCE_INLINE VariantRef get_impl(TKey key) const { FORCE_INLINE VariantRef get_impl(TKey key) const {
@ -270,8 +228,8 @@ class ObjectRef : public ObjectRefBase<CollectionData>, public Visitable {
} }
template <typename TKey> template <typename TKey>
FORCE_INLINE VariantRef set_impl(TKey key) const { FORCE_INLINE VariantRef getOrCreate_impl(TKey key) const {
return VariantRef(_pool, objectSet(_data, key, _pool)); return VariantRef(_pool, objectGetOrCreate(_data, key, _pool));
} }
MemoryPool* _pool; MemoryPool* _pool;

View File

@ -0,0 +1,61 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#pragma once
#include "../Polyfills/attributes.hpp"
#include "../Polyfills/type_traits.hpp"
#include "../Strings/StringWrappers.hpp"
namespace ARDUINOJSON_NAMESPACE {
template <typename TParent, typename TKey>
class MemberProxy;
template <typename TObject>
class ObjectShortcuts {
public:
// MemberProxy operator[](TKey) const;
// TKey = const std::string&, const String&
template <typename TKey>
FORCE_INLINE
typename enable_if<IsString<TKey>::value,
MemberProxy<const TObject &, const TKey &> >::type
operator[](const TKey &key) const;
//
// MemberProxy operator[](TKey) const;
// TKey = const char*, const char[N], const __FlashStringHelper*
template <typename TKey>
FORCE_INLINE typename enable_if<IsString<TKey *>::value,
MemberProxy<const TObject &, TKey *> >::type
operator[](TKey *key) const;
// Creates and adds a ArrayRef.
//
// ArrayRef createNestedArray(TKey);
// TKey = const std::string&, const String&
template <typename TKey>
FORCE_INLINE ArrayRef createNestedArray(const TKey &key) const;
// ArrayRef createNestedArray(TKey);
// TKey = char*, const char*, char[], const char[], const __FlashStringHelper*
template <typename TKey>
FORCE_INLINE ArrayRef createNestedArray(TKey *key) const;
// Creates and adds a ObjectRef.
//
// ObjectRef createNestedObject(TKey);
// TKey = const std::string&, const String&
template <typename TKey>
ObjectRef createNestedObject(const TKey &key) const;
//
// ObjectRef createNestedObject(TKey);
// TKey = char*, const char*, char[], const char[], const __FlashStringHelper*
template <typename TKey>
ObjectRef createNestedObject(TKey *key) const;
private:
const TObject *impl() const {
return static_cast<const TObject *>(this);
}
};
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -7,7 +7,7 @@
#include "VariantCasts.hpp" #include "VariantCasts.hpp"
#include "VariantComparisons.hpp" #include "VariantComparisons.hpp"
#include "VariantOr.hpp" #include "VariantOr.hpp"
#include "VariantSubscripts.hpp" #include "VariantShortcuts.hpp"
namespace ARDUINOJSON_NAMESPACE { namespace ARDUINOJSON_NAMESPACE {
@ -15,5 +15,5 @@ template <typename TImpl>
class VariantOperators : public VariantCasts<TImpl>, class VariantOperators : public VariantCasts<TImpl>,
public VariantComparisons<TImpl>, public VariantComparisons<TImpl>,
public VariantOr<TImpl>, public VariantOr<TImpl>,
public VariantSubscripts<TImpl> {}; public VariantShortcuts<TImpl> {};
} // namespace ARDUINOJSON_NAMESPACE } // namespace ARDUINOJSON_NAMESPACE

View File

@ -0,0 +1,23 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#pragma once
#include "../Array/ArrayShortcuts.hpp"
#include "../Object/ObjectShortcuts.hpp"
namespace ARDUINOJSON_NAMESPACE {
template <typename TVariant>
class VariantShortcuts : public ObjectShortcuts<TVariant>,
public ArrayShortcuts<TVariant> {
public:
using ArrayShortcuts<TVariant>::createNestedArray;
using ArrayShortcuts<TVariant>::createNestedObject;
using ArrayShortcuts<TVariant>::operator[];
using ObjectShortcuts<TVariant>::createNestedArray;
using ObjectShortcuts<TVariant>::createNestedObject;
using ObjectShortcuts<TVariant>::operator[];
};
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -1,51 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#pragma once
#include "../Polyfills/attributes.hpp"
#include "../Polyfills/type_traits.hpp"
#include "../Strings/StringWrappers.hpp"
#include "../Variant/VariantAs.hpp"
namespace ARDUINOJSON_NAMESPACE {
class ArrayRef;
class ObjectRef;
// Forward declarations.
class ArraySubscript;
template <typename TKey>
class ObjectSubscript;
template <typename TImpl>
class VariantSubscripts {
public:
// Mimics an array.
// Returns the element at specified index if the variant is an array.
FORCE_INLINE ArraySubscript operator[](size_t index) const;
// Mimics an object.
// Returns the value associated with the specified key if the variant is
// an object.
//
// ObjectSubscript operator[](TKey) const;
// TKey = const std::string&, const String&
template <typename TKey>
FORCE_INLINE typename enable_if<IsString<TKey>::value,
ObjectSubscript<const TKey &> >::type
operator[](const TKey &key) const;
//
// ObjectSubscript operator[](TKey) const;
// TKey = const char*, const char[N], const __FlashStringHelper*
template <typename TKey>
FORCE_INLINE typename enable_if<IsString<TKey *>::value,
ObjectSubscript<TKey *> >::type
operator[](TKey *key) const;
private:
const TImpl *impl() const {
return static_cast<const TImpl *>(this);
}
};
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -12,19 +12,24 @@
namespace ARDUINOJSON_NAMESPACE { namespace ARDUINOJSON_NAMESPACE {
//
enum { enum {
VALUE_IS_NULL = 0,
VALUE_IS_LINKED_RAW,
VALUE_IS_OWNED_RAW,
VALUE_IS_LINKED_STRING,
VALUE_IS_OWNED_STRING,
VALUE_IS_BOOLEAN,
VALUE_IS_POSITIVE_INTEGER,
VALUE_IS_NEGATIVE_INTEGER,
VALUE_IS_ARRAY,
VALUE_IS_OBJECT,
VALUE_IS_FLOAT,
VALUE_MASK = 0x7F, VALUE_MASK = 0x7F,
VALUE_IS_NULL = 0,
VALUE_IS_LINKED_RAW = 0x01,
VALUE_IS_OWNED_RAW = 0x02,
VALUE_IS_LINKED_STRING = 0x03,
VALUE_IS_OWNED_STRING = 0x04,
VALUE_IS_BOOLEAN = 0x05,
VALUE_IS_POSITIVE_INTEGER = 0x06,
VALUE_IS_NEGATIVE_INTEGER = 0x07,
VALUE_IS_FLOAT = 0x08,
COLLECTION_MASK = 0x60,
VALUE_IS_OBJECT = 0x20,
VALUE_IS_ARRAY = 0x40,
KEY_IS_OWNED = 0x80 KEY_IS_OWNED = 0x80
}; };

View File

@ -67,7 +67,7 @@ class VariantData {
} }
CollectionData *asArray() { CollectionData *asArray() {
return type() == VALUE_IS_ARRAY ? &_content.asCollection : 0; return isArray() ? &_content.asCollection : 0;
} }
const CollectionData *asArray() const { const CollectionData *asArray() const {
@ -75,7 +75,7 @@ class VariantData {
} }
CollectionData *asObject() { CollectionData *asObject() {
return type() == VALUE_IS_OBJECT ? &_content.asCollection : 0; return isObject() ? &_content.asCollection : 0;
} }
const CollectionData *asObject() const { const CollectionData *asObject() const {
@ -135,13 +135,17 @@ class VariantData {
} }
bool isArray() const { bool isArray() const {
return type() == VALUE_IS_ARRAY; return (_flags & VALUE_IS_ARRAY) != 0;
} }
bool isBoolean() const { bool isBoolean() const {
return type() == VALUE_IS_BOOLEAN; return type() == VALUE_IS_BOOLEAN;
} }
bool isCollection() const {
return (_flags & COLLECTION_MASK) != 0;
}
bool isInteger() const { bool isInteger() const {
return type() == VALUE_IS_POSITIVE_INTEGER || return type() == VALUE_IS_POSITIVE_INTEGER ||
type() == VALUE_IS_NEGATIVE_INTEGER; type() == VALUE_IS_NEGATIVE_INTEGER;
@ -153,12 +157,11 @@ class VariantData {
} }
bool isString() const { bool isString() const {
return (type() == VALUE_IS_LINKED_STRING || return type() == VALUE_IS_LINKED_STRING || type() == VALUE_IS_OWNED_STRING;
type() == VALUE_IS_OWNED_STRING);
} }
bool isObject() const { bool isObject() const {
return type() == VALUE_IS_OBJECT; return (_flags & VALUE_IS_OBJECT) != 0;
} }
bool isNull() const { bool isNull() const {
@ -275,20 +278,35 @@ class VariantData {
} }
size_t nesting() const { size_t nesting() const {
switch (type()) { return isCollection() ? _content.asCollection.nesting() : 0;
case VALUE_IS_OBJECT:
case VALUE_IS_ARRAY:
return _content.asCollection.nesting();
default:
return 0;
}
} }
size_t size() const { size_t size() const {
if (type() == VALUE_IS_OBJECT || type() == VALUE_IS_ARRAY) return isCollection() ? _content.asCollection.size() : 0;
return _content.asCollection.size(); }
else
return 0; VariantData *get(size_t index) const {
return isArray() ? _content.asCollection.get(index) : 0;
}
template <typename TKey>
VariantData *get(TKey key) const {
return isObject() ? _content.asCollection.get(key) : 0;
}
template <typename TKey>
VariantData *getOrCreate(TKey key, MemoryPool *pool) {
if (isNull()) toObject();
if (!isObject()) return 0;
VariantData *var = _content.asCollection.get(key);
if (var) return var;
return _content.asCollection.add(key, pool);
}
VariantData *add(MemoryPool *pool) {
if (isNull()) toArray();
if (!isArray()) return 0;
return _content.asCollection.add(pool);
} }
private: private:

View File

@ -146,4 +146,20 @@ inline CollectionData *variantToObject(VariantData *var) {
return &var->toObject(); return &var->toObject();
} }
inline NO_INLINE VariantData *variantAdd(VariantData *var, MemoryPool *pool) {
return var != 0 ? var->add(pool) : 0;
}
template <typename TKey>
NO_INLINE VariantData *variantGetOrCreate(VariantData *var, TKey *key,
MemoryPool *pool) {
return var != 0 ? var->getOrCreate(wrapString(key), pool) : 0;
}
template <typename TKey>
NO_INLINE VariantData *variantGetOrCreate(VariantData *var, const TKey &key,
MemoryPool *pool) {
return var != 0 ? var->getOrCreate(wrapString(key), pool) : 0;
}
} // namespace ARDUINOJSON_NAMESPACE } // namespace ARDUINOJSON_NAMESPACE

View File

@ -60,37 +60,11 @@ inline const char* VariantData::asString() const {
} }
} }
inline bool VariantRef::set(ArrayRef array) const { template <typename TVariant>
return to<ArrayRef>().copyFrom(array); typename enable_if<IsVisitable<TVariant>::value, bool>::type VariantRef::set(
} const TVariant &value) const {
VariantConstRef v = value;
inline bool VariantRef::set(ArrayConstRef array) const { return variantCopyFrom(_data, v._data, _pool);
return to<ArrayRef>().copyFrom(array);
}
inline bool VariantRef::set(const ArraySubscript& value) const {
return set(value.as<VariantRef>());
}
inline bool VariantRef::set(ObjectRef object) const {
return to<ObjectRef>().copyFrom(object);
}
inline bool VariantRef::set(ObjectConstRef object) const {
return to<ObjectRef>().copyFrom(object);
}
template <typename TString>
inline bool VariantRef::set(const ObjectSubscript<TString>& value) const {
return set(value.template as<VariantRef>());
}
inline bool VariantRef::set(VariantConstRef value) const {
return variantCopyFrom(_data, value._data, _pool);
}
inline bool VariantRef::set(VariantRef value) const {
return variantCopyFrom(_data, value._data, _pool);
} }
template <typename T> template <typename T>
@ -128,4 +102,32 @@ inline VariantConstRef VariantConstRef::operator[](size_t index) const {
return ArrayConstRef(_data != 0 ? _data->asArray() : 0)[index]; return ArrayConstRef(_data != 0 ? _data->asArray() : 0)[index];
} }
inline VariantRef VariantRef::add() const {
return VariantRef(_pool, variantAdd(_data, _pool));
}
inline VariantRef VariantRef::get(size_t index) const {
return VariantRef(_pool, _data != 0 ? _data->get(index) : 0);
}
template <typename TKey>
inline VariantRef VariantRef::get(TKey *key) const {
return VariantRef(_pool, _data != 0 ? _data->get(wrapString(key)) : 0);
}
template <typename TKey>
inline typename enable_if<IsString<TKey>::value, VariantRef>::type
VariantRef::get(const TKey &key) const {
return VariantRef(_pool, _data != 0 ? _data->get(wrapString(key)) : 0);
}
template <typename TKey>
inline VariantRef VariantRef::getOrCreate(TKey *key) const {
return VariantRef(_pool, variantGetOrCreate(_data, key, _pool));
}
template <typename TKey>
inline VariantRef VariantRef::getOrCreate(const TKey &key) const {
return VariantRef(_pool, variantGetOrCreate(_data, key, _pool));
}
} // namespace ARDUINOJSON_NAMESPACE } // namespace ARDUINOJSON_NAMESPACE

View File

@ -23,6 +23,9 @@ namespace ARDUINOJSON_NAMESPACE {
class ArrayRef; class ArrayRef;
class ObjectRef; class ObjectRef;
template <typename, typename>
class MemberProxy;
// Contains the methods shared by VariantRef and VariantConstRef // Contains the methods shared by VariantRef and VariantConstRef
template <typename TData> template <typename TData>
class VariantRefBase { class VariantRefBase {
@ -207,16 +210,15 @@ class VariantRef : public VariantRefBase<VariantData>,
return variantSetLinkedString(_data, value); return variantSetLinkedString(_data, value);
} }
bool set(VariantConstRef value) const; // set(VariantRef)
bool set(VariantRef value) const; // set(VariantConstRef)
// set(ArrayRef)
FORCE_INLINE bool set(ArrayRef array) const; // set(ArrayConstRef)
FORCE_INLINE bool set(ArrayConstRef array) const; // set(ObjectRef)
FORCE_INLINE bool set(const ArraySubscript &) const; // set(ObjecConstRef)
FORCE_INLINE bool set(ObjectRef object) const; template <typename TVariant>
FORCE_INLINE bool set(ObjectConstRef object) const; typename enable_if<IsVisitable<TVariant>::value, bool>::type set(
template <typename TString> const TVariant &value) const;
FORCE_INLINE bool set(const ObjectSubscript<TString> &) const;
// Get the variant as the specified type. // Get the variant as the specified type.
// //
@ -278,6 +280,26 @@ class VariantRef : public VariantRefBase<VariantData>,
typename enable_if<is_same<T, VariantRef>::value, VariantRef>::type to() typename enable_if<is_same<T, VariantRef>::value, VariantRef>::type to()
const; const;
VariantRef add() const;
using ArrayShortcuts::add;
FORCE_INLINE VariantRef get(size_t) const;
template <typename TKey>
FORCE_INLINE VariantRef get(TKey *) const;
// get(const char*)
// get(const __FlashStringHelper*)
template <typename TKey>
FORCE_INLINE typename enable_if<IsString<TKey>::value, VariantRef>::type get(
const TKey &) const;
template <typename TKey>
FORCE_INLINE VariantRef getOrCreate(TKey *) const;
template <typename TKey>
FORCE_INLINE VariantRef getOrCreate(const TKey &) const;
private: private:
MemoryPool *_pool; MemoryPool *_pool;
}; };

View File

@ -71,6 +71,7 @@ if(MSVC)
) )
endif() endif()
add_subdirectory(ElementProxy)
add_subdirectory(IntegrationTests) add_subdirectory(IntegrationTests)
add_subdirectory(JsonArray) add_subdirectory(JsonArray)
add_subdirectory(JsonDeserializer) add_subdirectory(JsonDeserializer)
@ -78,10 +79,11 @@ add_subdirectory(JsonDocument)
add_subdirectory(JsonObject) add_subdirectory(JsonObject)
add_subdirectory(JsonSerializer) add_subdirectory(JsonSerializer)
add_subdirectory(JsonVariant) add_subdirectory(JsonVariant)
add_subdirectory(TextFormatter) add_subdirectory(MemberProxy)
add_subdirectory(MemoryPool) add_subdirectory(MemoryPool)
add_subdirectory(Misc) add_subdirectory(Misc)
add_subdirectory(MixedConfiguration) add_subdirectory(MixedConfiguration)
add_subdirectory(MsgPackDeserializer) add_subdirectory(MsgPackDeserializer)
add_subdirectory(MsgPackSerializer) add_subdirectory(MsgPackSerializer)
add_subdirectory(Numbers) add_subdirectory(Numbers)
add_subdirectory(TextFormatter)

View File

@ -0,0 +1,11 @@
# ArduinoJson - arduinojson.org
# Copyright Benoit Blanchon 2014-2018
# MIT License
add_executable(ElementProxyTests
add.cpp
set.cpp
)
target_link_libraries(ElementProxyTests catch)
add_test(ElementProxy ElementProxyTests)

26
test/ElementProxy/add.cpp Normal file
View File

@ -0,0 +1,26 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("ElementProxy::add()") {
DynamicJsonDocument doc(4096);
doc.add();
ElementProxy<JsonDocument&> ep = doc[0];
SECTION("add(int)") {
ep.add(42);
REQUIRE(doc.as<std::string>() == "[[42]]");
}
SECTION("add(const char*)") {
ep.add("world");
REQUIRE(doc.as<std::string>() == "[[\"world\"]]");
}
}

26
test/ElementProxy/set.cpp Normal file
View File

@ -0,0 +1,26 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("ElementProxy::set()") {
DynamicJsonDocument doc(4096);
doc.add();
ElementProxy<JsonDocument&> ep = doc[0];
SECTION("set(int)") {
ep.set(42);
REQUIRE(doc.as<std::string>() == "[42]");
}
SECTION("set(const char*)") {
ep.set("world");
REQUIRE(doc.as<std::string>() == "[\"world\"]");
}
}

View File

@ -3,9 +3,11 @@
# MIT License # MIT License
add_executable(JsonDocumentTests add_executable(JsonDocumentTests
add.cpp
createNested.cpp
DynamicJsonDocument.cpp DynamicJsonDocument.cpp
nesting.cpp
isNull.cpp isNull.cpp
nesting.cpp
StaticJsonDocument.cpp StaticJsonDocument.cpp
subscript.cpp subscript.cpp
) )

22
test/JsonDocument/add.cpp Normal file
View File

@ -0,0 +1,22 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonDocument::add()") {
DynamicJsonDocument doc(4096);
SECTION("integer") {
doc.add(42);
REQUIRE(doc.as<std::string>() == "[42]");
}
SECTION("const char*") {
doc.add("hello");
REQUIRE(doc.as<std::string>() == "[\"hello\"]");
}
}

View File

@ -0,0 +1,66 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonDocument::createNestedArray()") {
DynamicJsonDocument doc(4096);
SECTION("promotes to array") {
doc.createNestedArray();
REQUIRE(doc.is<JsonArray>());
}
}
TEST_CASE("JsonDocument::createNestedArray(key)") {
DynamicJsonDocument doc(4096);
SECTION("key is const char*") {
SECTION("promotes to object") {
doc.createNestedArray("hello");
REQUIRE(doc.is<JsonObject>());
}
}
SECTION("key is std::string") {
SECTION("promotes to object") {
doc.createNestedArray(std::string("hello"));
REQUIRE(doc.is<JsonObject>());
}
}
}
TEST_CASE("JsonDocument::createNestedObject()") {
DynamicJsonDocument doc(4096);
SECTION("promotes to array") {
doc.createNestedObject();
REQUIRE(doc.is<JsonArray>());
}
}
TEST_CASE("JsonDocument::createNestedObject(key)") {
DynamicJsonDocument doc(4096);
SECTION("key is const char*") {
SECTION("promotes to object") {
doc.createNestedObject("hello");
REQUIRE(doc.is<JsonObject>());
}
}
SECTION("key is std::string") {
SECTION("promotes to object") {
doc.createNestedObject(std::string("hello"));
REQUIRE(doc.is<JsonObject>());
}
}
}

View File

@ -21,6 +21,11 @@ TEST_CASE("JsonDocument::operator[]") {
REQUIRE(doc[std::string("hello")] == "world"); REQUIRE(doc[std::string("hello")] == "world");
REQUIRE(cdoc[std::string("hello")] == "world"); REQUIRE(cdoc[std::string("hello")] == "world");
} }
SECTION("supports operator|") {
REQUIRE((doc["hello"] | "nope") == std::string("world"));
REQUIRE((doc["world"] | "nope") == std::string("nope"));
}
} }
SECTION("array") { SECTION("array") {
@ -30,3 +35,11 @@ TEST_CASE("JsonDocument::operator[]") {
REQUIRE(cdoc[1] == "world"); REQUIRE(cdoc[1] == "world");
} }
} }
TEST_CASE("JsonDocument automatically promotes to object") {
DynamicJsonDocument doc(4096);
doc["one"]["two"]["three"] = 4;
REQUIRE(doc["one"]["two"]["three"] == 4);
}

View File

@ -12,7 +12,7 @@ void check(T value, const std::string &expected) {
REQUIRE(expected.size() == returnValue); REQUIRE(expected.size() == returnValue);
} }
TEST_CASE("serializeJson(JsonObjectSubscript)") { TEST_CASE("serializeJson(MemberProxy)") {
DynamicJsonDocument doc(4096); DynamicJsonDocument doc(4096);
deserializeJson(doc, "{\"hello\":42}"); deserializeJson(doc, "{\"hello\":42}");
JsonObject obj = doc.as<JsonObject>(); JsonObject obj = doc.as<JsonObject>();
@ -23,7 +23,7 @@ TEST_CASE("serializeJson(JsonObjectSubscript)") {
REQUIRE(result == "42"); REQUIRE(result == "42");
} }
TEST_CASE("serializeJson(JsonArraySubscript)") { TEST_CASE("serializeJson(ElementProxy)") {
DynamicJsonDocument doc(4096); DynamicJsonDocument doc(4096);
deserializeJson(doc, "[42]"); deserializeJson(doc, "[42]");
JsonArray arr = doc.as<JsonArray>(); JsonArray arr = doc.as<JsonArray>();

View File

@ -37,7 +37,7 @@ TEST_CASE("operator<<(std::ostream)") {
REQUIRE("{\"key\":\"value\"}" == os.str()); REQUIRE("{\"key\":\"value\"}" == os.str());
} }
SECTION("JsonObjectSubscript") { SECTION("MemberProxy") {
JsonObject object = doc.to<JsonObject>(); JsonObject object = doc.to<JsonObject>();
object["key"] = "value"; object["key"] = "value";
@ -55,7 +55,7 @@ TEST_CASE("operator<<(std::ostream)") {
REQUIRE("[\"value\"]" == os.str()); REQUIRE("[\"value\"]" == os.str());
} }
SECTION("JsonArraySubscript") { SECTION("ElementProxy") {
JsonArray array = doc.to<JsonArray>(); JsonArray array = doc.to<JsonArray>();
array.add("value"); array.add("value");

View File

@ -3,18 +3,21 @@
# MIT License # MIT License
add_executable(JsonVariantTests add_executable(JsonVariantTests
add.cpp
as.cpp as.cpp
compare.cpp compare.cpp
copy.cpp copy.cpp
createNested.cpp
get.cpp get.cpp
is.cpp is.cpp
isnull.cpp isnull.cpp
memoryUsage.cpp memoryUsage.cpp
nesting.cpp
misc.cpp misc.cpp
nesting.cpp
or.cpp or.cpp
set.cpp set.cpp
subscript.cpp subscript.cpp
types.cpp
undefined.cpp undefined.cpp
) )

39
test/JsonVariant/add.cpp Normal file
View File

@ -0,0 +1,39 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson.h>
#include <stdint.h>
#include <catch.hpp>
static const char* null = 0;
TEST_CASE("JsonVariant::add()") {
DynamicJsonDocument doc(4096);
JsonVariant var = doc.to<JsonVariant>();
SECTION("No argument") {
JsonVariant nested = var.add();
REQUIRE(var.is<JsonArray>() == true);
REQUIRE(nested.isNull() == true);
}
SECTION("integer") {
var.add(42);
REQUIRE(var.as<std::string>() == "[42]");
}
SECTION("const char*") {
var.add("hello");
REQUIRE(var.as<std::string>() == "[\"hello\"]");
}
SECTION("std::string") {
var.add(std::string("hello"));
REQUIRE(var.as<std::string>() == "[\"hello\"]");
}
}

View File

@ -0,0 +1,88 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson.h>
#include <stdint.h>
#include <catch.hpp>
static const char* null = 0;
TEST_CASE("JsonVariant::createNestedObject()") {
DynamicJsonDocument doc(4096);
JsonVariant variant = doc.to<JsonVariant>();
SECTION("promotes to array") {
JsonObject obj = variant.createNestedObject();
obj["value"] = "42";
REQUIRE(variant.is<JsonArray>() == true);
REQUIRE(variant[0]["value"] == 42);
REQUIRE(obj.isNull() == false);
}
SECTION("works on MemberProxy") {
JsonObject obj = variant["items"].createNestedObject();
obj["value"] = "42";
REQUIRE(variant["items"][0]["value"] == 42);
}
}
TEST_CASE("JsonVariant::createNestedArray()") {
DynamicJsonDocument doc(4096);
JsonVariant variant = doc.to<JsonVariant>();
SECTION("promotes to array") {
JsonArray arr = variant.createNestedArray();
REQUIRE(variant.is<JsonArray>() == true);
REQUIRE(arr.isNull() == false);
}
SECTION("works on MemberProxy") {
JsonArray arr = variant["items"].createNestedArray();
arr.add("42");
REQUIRE(variant["items"][0][0] == 42);
}
}
TEST_CASE("JsonVariant::createNestedObject(key)") {
DynamicJsonDocument doc(4096);
JsonVariant variant = doc.to<JsonVariant>();
SECTION("promotes to object") {
JsonObject obj = variant.createNestedObject("weather");
obj["temp"] = "42";
REQUIRE(variant.is<JsonObject>() == true);
REQUIRE(variant["weather"]["temp"] == 42);
}
SECTION("works on MemberProxy") {
JsonObject obj = variant["status"].createNestedObject("weather");
obj["temp"] = "42";
REQUIRE(variant["status"]["weather"]["temp"] == 42);
}
}
TEST_CASE("JsonVariant::createNestedArray(key)") {
DynamicJsonDocument doc(4096);
JsonVariant variant = doc.to<JsonVariant>();
SECTION("promotes to object") {
JsonArray arr = variant.createNestedArray("items");
REQUIRE(variant.is<JsonObject>() == true);
REQUIRE(arr.isNull() == false);
}
SECTION("works on MemberProxy") {
JsonArray arr = variant["weather"].createNestedArray("temp");
arr.add("42");
REQUIRE(variant["weather"]["temp"][0] == 42);
}
}

View File

@ -3,138 +3,27 @@
// MIT License // MIT License
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <stdint.h>
#include <catch.hpp> #include <catch.hpp>
#include <limits>
template <typename T> TEST_CASE("JsonVariant::get()") {
void checkValue(T expected) {
DynamicJsonDocument doc(4096); DynamicJsonDocument doc(4096);
JsonVariant variant = doc.to<JsonVariant>(); JsonVariant var = doc.to<JsonVariant>();
variant.set(expected); SECTION("get(const char*)") {
REQUIRE(expected == variant.as<T>()); var["value"] = 42;
REQUIRE(var.get("value") == 42);
} }
template <typename T> SECTION("get(std::string)") {
void checkReference(T &expected) { var["value"] = 42;
JsonVariant variant = expected;
REQUIRE(expected == variant.as<T &>()); REQUIRE(var.get(std::string("value")) == 42);
} }
template <typename T> SECTION("get(int)") {
void checkNumericType() { var.add().set(42);
DynamicJsonDocument docMin(4096), docMax(4096);
JsonVariant variantMin = docMin.to<JsonVariant>();
JsonVariant variantMax = docMax.to<JsonVariant>();
T min = std::numeric_limits<T>::min(); REQUIRE(var.get(0) == 42);
T max = std::numeric_limits<T>::max();
variantMin.set(min);
variantMax.set(max);
REQUIRE(min == variantMin.as<T>());
REQUIRE(max == variantMax.as<T>());
}
TEST_CASE("JsonVariant set()/get()") {
#if ARDUINOJSON_USE_LONG_LONG
SECTION("SizeOfJsonInteger") {
REQUIRE(8 == sizeof(JsonInteger));
}
#endif
SECTION("Null") {
checkValue<const char *>(NULL);
}
SECTION("const char*") {
checkValue<const char *>("hello");
}
SECTION("std::string") {
checkValue<std::string>("hello");
}
SECTION("False") {
checkValue<bool>(false);
}
SECTION("True") {
checkValue<bool>(true);
}
SECTION("Double") {
checkNumericType<double>();
}
SECTION("Float") {
checkNumericType<float>();
}
SECTION("Char") {
checkNumericType<char>();
}
SECTION("SChar") {
checkNumericType<signed char>();
}
SECTION("SInt") {
checkNumericType<signed int>();
}
SECTION("SLong") {
checkNumericType<signed long>();
}
SECTION("SShort") {
checkNumericType<signed short>();
}
SECTION("UChar") {
checkNumericType<unsigned char>();
}
SECTION("UInt") {
checkNumericType<unsigned int>();
}
SECTION("ULong") {
checkNumericType<unsigned long>();
}
SECTION("UShort") {
checkNumericType<unsigned short>();
}
#if ARDUINOJSON_USE_LONG_LONG
SECTION("LongLong") {
checkNumericType<unsigned long long>();
}
SECTION("ULongLong") {
checkNumericType<unsigned long long>();
}
#endif
SECTION("Int8") {
checkNumericType<int8_t>();
}
SECTION("Uint8") {
checkNumericType<uint8_t>();
}
SECTION("Int16") {
checkNumericType<int16_t>();
}
SECTION("Uint16") {
checkNumericType<uint16_t>();
}
SECTION("Int32") {
checkNumericType<int32_t>();
}
SECTION("Uint32") {
checkNumericType<uint32_t>();
}
#if ARDUINOJSON_USE_LONG_LONG
SECTION("Int64") {
checkNumericType<int64_t>();
}
SECTION("Uint64") {
checkNumericType<uint64_t>();
}
#endif
SECTION("CanStoreObject") {
DynamicJsonDocument doc(4096);
JsonObject object = doc.to<JsonObject>();
checkValue<JsonObject>(object);
} }
} }

View File

@ -30,7 +30,8 @@ TEST_CASE("JsonVariant::operator[]") {
array.add("element at index 1"); array.add("element at index 1");
REQUIRE(2 == var.size()); REQUIRE(2 == var.size());
REQUIRE(std::string("element at index 0") == var[0]); var[0].as<std::string>();
// REQUIRE(std::string("element at index 0") == );
REQUIRE(std::string("element at index 1") == var[1]); REQUIRE(std::string("element at index 1") == var[1]);
REQUIRE(std::string("element at index 0") == REQUIRE(std::string("element at index 0") ==
var[static_cast<unsigned char>(0)]); // issue #381 var[static_cast<unsigned char>(0)]); // issue #381
@ -171,4 +172,24 @@ TEST_CASE("JsonVariantConst::operator[]") {
REQUIRE(cvar[0].isNull()); REQUIRE(cvar[0].isNull());
} }
} }
SECTION("Auto promote null JsonVariant to JsonObject") {
var["hello"] = "world";
REQUIRE(var.is<JsonObject>() == true);
}
SECTION("Don't auto promote non-null JsonVariant to JsonObject") {
var.set(42);
var["hello"] = "world";
REQUIRE(var.is<JsonObject>() == false);
}
SECTION("Don't auto promote null JsonVariant to JsonObject when reading") {
const char* value = var["hello"];
REQUIRE(var.is<JsonObject>() == false);
REQUIRE(value == 0);
}
} }

140
test/JsonVariant/types.cpp Normal file
View File

@ -0,0 +1,140 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson.h>
#include <stdint.h>
#include <catch.hpp>
#include <limits>
template <typename T>
void checkValue(T expected) {
DynamicJsonDocument doc(4096);
JsonVariant variant = doc.to<JsonVariant>();
variant.set(expected);
REQUIRE(expected == variant.as<T>());
}
template <typename T>
void checkReference(T &expected) {
JsonVariant variant = expected;
REQUIRE(expected == variant.as<T &>());
}
template <typename T>
void checkNumericType() {
DynamicJsonDocument docMin(4096), docMax(4096);
JsonVariant variantMin = docMin.to<JsonVariant>();
JsonVariant variantMax = docMax.to<JsonVariant>();
T min = std::numeric_limits<T>::min();
T max = std::numeric_limits<T>::max();
variantMin.set(min);
variantMax.set(max);
REQUIRE(min == variantMin.as<T>());
REQUIRE(max == variantMax.as<T>());
}
TEST_CASE("JsonVariant set()/get()") {
#if ARDUINOJSON_USE_LONG_LONG
SECTION("SizeOfJsonInteger") {
REQUIRE(8 == sizeof(JsonInteger));
}
#endif
SECTION("Null") {
checkValue<const char *>(NULL);
}
SECTION("const char*") {
checkValue<const char *>("hello");
}
SECTION("std::string") {
checkValue<std::string>("hello");
}
SECTION("False") {
checkValue<bool>(false);
}
SECTION("True") {
checkValue<bool>(true);
}
SECTION("Double") {
checkNumericType<double>();
}
SECTION("Float") {
checkNumericType<float>();
}
SECTION("Char") {
checkNumericType<char>();
}
SECTION("SChar") {
checkNumericType<signed char>();
}
SECTION("SInt") {
checkNumericType<signed int>();
}
SECTION("SLong") {
checkNumericType<signed long>();
}
SECTION("SShort") {
checkNumericType<signed short>();
}
SECTION("UChar") {
checkNumericType<unsigned char>();
}
SECTION("UInt") {
checkNumericType<unsigned int>();
}
SECTION("ULong") {
checkNumericType<unsigned long>();
}
SECTION("UShort") {
checkNumericType<unsigned short>();
}
#if ARDUINOJSON_USE_LONG_LONG
SECTION("LongLong") {
checkNumericType<unsigned long long>();
}
SECTION("ULongLong") {
checkNumericType<unsigned long long>();
}
#endif
SECTION("Int8") {
checkNumericType<int8_t>();
}
SECTION("Uint8") {
checkNumericType<uint8_t>();
}
SECTION("Int16") {
checkNumericType<int16_t>();
}
SECTION("Uint16") {
checkNumericType<uint16_t>();
}
SECTION("Int32") {
checkNumericType<int32_t>();
}
SECTION("Uint32") {
checkNumericType<uint32_t>();
}
#if ARDUINOJSON_USE_LONG_LONG
SECTION("Int64") {
checkNumericType<int64_t>();
}
SECTION("Uint64") {
checkNumericType<uint64_t>();
}
#endif
SECTION("CanStoreObject") {
DynamicJsonDocument doc(4096);
JsonObject object = doc.to<JsonObject>();
checkValue<JsonObject>(object);
}
}

View File

@ -0,0 +1,12 @@
# ArduinoJson - arduinojson.org
# Copyright Benoit Blanchon 2014-2018
# MIT License
add_executable(MemberProxyTests
add.cpp
subscript.cpp
set.cpp
)
target_link_libraries(MemberProxyTests catch)
add_test(MemberProxy MemberProxyTests)

25
test/MemberProxy/add.cpp Normal file
View File

@ -0,0 +1,25 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("MemberProxy::add()") {
DynamicJsonDocument doc(4096);
MemberProxy<JsonDocument&, const char*> mp = doc["hello"];
SECTION("add(int)") {
mp.add(42);
REQUIRE(doc.as<std::string>() == "{\"hello\":[42]}");
}
SECTION("add(const char*)") {
mp.add("world");
REQUIRE(doc.as<std::string>() == "{\"hello\":[\"world\"]}");
}
}

25
test/MemberProxy/set.cpp Normal file
View File

@ -0,0 +1,25 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("MemberProxy::set()") {
DynamicJsonDocument doc(4096);
MemberProxy<JsonDocument&, const char*> mp = doc["hello"];
SECTION("set(int)") {
mp.set(42);
REQUIRE(doc.as<std::string>() == "{\"hello\":42}");
}
SECTION("set(const char*)") {
mp.set("world");
REQUIRE(doc.as<std::string>() == "{\"hello\":\"world\"}");
}
}

View File

@ -0,0 +1,19 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("MemberProxy::operator[]") {
DynamicJsonDocument doc(4096);
MemberProxy<JsonDocument&, const char*> mp = doc["hello"];
SECTION("set integer") {
mp["world"] = 42;
REQUIRE(doc.as<std::string>() == "{\"hello\":{\"world\":42}}");
}
}

View File

@ -54,10 +54,10 @@ TEST_CASE("Polyfills/type_traits") {
CHECK(IsVisitable<VariantRef>::value == true); CHECK(IsVisitable<VariantRef>::value == true);
CHECK(IsVisitable<VariantConstRef>::value == true); CHECK(IsVisitable<VariantConstRef>::value == true);
CHECK(IsVisitable<ArrayRef>::value == true); CHECK(IsVisitable<ArrayRef>::value == true);
CHECK(IsVisitable<ArraySubscript>::value == true); CHECK(IsVisitable<ElementProxy<ArrayRef> >::value == true);
CHECK(IsVisitable<ArrayConstRef>::value == true); CHECK(IsVisitable<ArrayConstRef>::value == true);
CHECK(IsVisitable<ObjectRef>::value == true); CHECK(IsVisitable<ObjectRef>::value == true);
CHECK(IsVisitable<ObjectSubscript<const char*> >::value == true); CHECK((IsVisitable<MemberProxy<ObjectRef, const char*> >::value == true));
CHECK(IsVisitable<ObjectConstRef>::value == true); CHECK(IsVisitable<ObjectConstRef>::value == true);
CHECK(IsVisitable<DynamicJsonDocument>::value == true); CHECK(IsVisitable<DynamicJsonDocument>::value == true);
CHECK(IsVisitable<StaticJsonDocument<10> >::value == true); CHECK(IsVisitable<StaticJsonDocument<10> >::value == true);

View File

@ -147,7 +147,7 @@ TEST_CASE("unsigned char[]") {
} }
} }
SECTION("JsonObjectSubscript") { SECTION("MemberProxy") {
SECTION("operator=") { // issue #416 SECTION("operator=") { // issue #416
unsigned char value[] = "world"; unsigned char value[] = "world";
@ -181,7 +181,7 @@ TEST_CASE("unsigned char[]") {
} }
} }
SECTION("JsonArraySubscript") { SECTION("ElementProxy") {
SECTION("set()") { SECTION("set()") {
unsigned char value[] = "world"; unsigned char value[] = "world";

View File

@ -12,7 +12,7 @@ void check(T value, const std::string &expected) {
REQUIRE(expected.size() == returnValue); REQUIRE(expected.size() == returnValue);
} }
TEST_CASE("serializeMsgPack(JsonObjectSubscript)") { TEST_CASE("serializeMsgPack(MemberProxy)") {
DynamicJsonDocument doc(4096); DynamicJsonDocument doc(4096);
deserializeJson(doc, "{\"hello\":42}"); deserializeJson(doc, "{\"hello\":42}");
JsonObject obj = doc.as<JsonObject>(); JsonObject obj = doc.as<JsonObject>();
@ -23,7 +23,7 @@ TEST_CASE("serializeMsgPack(JsonObjectSubscript)") {
REQUIRE(result == "*"); REQUIRE(result == "*");
} }
TEST_CASE("serializeMsgPack(JsonArraySubscript)") { TEST_CASE("serializeMsgPack(ElementProxy)") {
DynamicJsonDocument doc(4096); DynamicJsonDocument doc(4096);
deserializeJson(doc, "[42]"); deserializeJson(doc, "[42]");
JsonArray arr = doc.as<JsonArray>(); JsonArray arr = doc.as<JsonArray>();