From 6f55d1e58fdef814cef454526065fa40e9215080 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Tue, 29 Jan 2019 14:09:09 +0100 Subject: [PATCH] JsonVariant automatically promotes to JsonObject or JsonArray on write --- CHANGELOG.md | 6 + examples/JsonConfigFile/JsonConfigFile.ino | 40 +++-- .../JsonGeneratorExample.ino | 24 +-- examples/JsonHttpClient/JsonHttpClient.ino | 11 +- .../JsonParserExample/JsonParserExample.ino | 26 ++-- examples/JsonServer/JsonServer.ino | 31 ++-- examples/JsonUdpBeacon/JsonUdpBeacon.ino | 17 +-- examples/MsgPackParser/MsgPackParser.ino | 33 ++--- examples/ProgmemExample/ProgmemExample.ino | 9 +- examples/StringExample/StringExample.ino | 12 +- src/ArduinoJson.hpp | 4 +- src/ArduinoJson/Array/ArrayImpl.hpp | 11 +- src/ArduinoJson/Array/ArrayRef.hpp | 46 ++---- src/ArduinoJson/Array/ArrayShortcuts.hpp | 47 ++++++ .../{ArraySubscript.hpp => ElementProxy.hpp} | 77 ++++++---- src/ArduinoJson/Document/JsonDocument.hpp | 106 +++++++++++-- src/ArduinoJson/Misc/Visitable.hpp | 3 + .../{ObjectSubscript.hpp => MemberProxy.hpp} | 87 +++++++---- src/ArduinoJson/Object/ObjectFunctions.hpp | 3 +- src/ArduinoJson/Object/ObjectImpl.hpp | 26 +++- src/ArduinoJson/Object/ObjectRef.hpp | 74 ++------- src/ArduinoJson/Object/ObjectShortcuts.hpp | 61 ++++++++ .../Operators/VariantOperators.hpp | 4 +- .../Operators/VariantShortcuts.hpp | 23 +++ .../Operators/VariantSubscripts.hpp | 51 ------- src/ArduinoJson/Variant/VariantContent.hpp | 27 ++-- src/ArduinoJson/Variant/VariantData.hpp | 52 ++++--- src/ArduinoJson/Variant/VariantFunctions.hpp | 16 ++ src/ArduinoJson/Variant/VariantImpl.hpp | 66 +++++---- src/ArduinoJson/Variant/VariantRef.hpp | 42 ++++-- test/CMakeLists.txt | 4 +- test/ElementProxy/CMakeLists.txt | 11 ++ test/ElementProxy/add.cpp | 26 ++++ test/ElementProxy/set.cpp | 26 ++++ test/JsonDocument/CMakeLists.txt | 4 +- test/JsonDocument/add.cpp | 22 +++ test/JsonDocument/createNested.cpp | 66 +++++++++ test/JsonDocument/subscript.cpp | 13 ++ test/JsonSerializer/misc.cpp | 4 +- test/JsonSerializer/std_stream.cpp | 4 +- test/JsonVariant/CMakeLists.txt | 5 +- test/JsonVariant/add.cpp | 39 +++++ test/JsonVariant/createNested.cpp | 88 +++++++++++ test/JsonVariant/get.cpp | 135 ++--------------- test/JsonVariant/subscript.cpp | 23 ++- test/JsonVariant/types.cpp | 140 ++++++++++++++++++ test/MemberProxy/CMakeLists.txt | 12 ++ test/MemberProxy/add.cpp | 25 ++++ test/MemberProxy/set.cpp | 25 ++++ test/MemberProxy/subscript.cpp | 19 +++ test/Misc/TypeTraits.cpp | 4 +- test/Misc/unsigned_char.cpp | 4 +- test/MsgPackSerializer/misc.cpp | 4 +- 53 files changed, 1197 insertions(+), 541 deletions(-) create mode 100644 src/ArduinoJson/Array/ArrayShortcuts.hpp rename src/ArduinoJson/Array/{ArraySubscript.hpp => ElementProxy.hpp} (51%) rename src/ArduinoJson/Object/{ObjectSubscript.hpp => MemberProxy.hpp} (50%) create mode 100644 src/ArduinoJson/Object/ObjectShortcuts.hpp create mode 100644 src/ArduinoJson/Operators/VariantShortcuts.hpp delete mode 100644 src/ArduinoJson/Operators/VariantSubscripts.hpp create mode 100644 test/ElementProxy/CMakeLists.txt create mode 100644 test/ElementProxy/add.cpp create mode 100644 test/ElementProxy/set.cpp create mode 100644 test/JsonDocument/add.cpp create mode 100644 test/JsonDocument/createNested.cpp create mode 100644 test/JsonVariant/add.cpp create mode 100644 test/JsonVariant/createNested.cpp create mode 100644 test/JsonVariant/types.cpp create mode 100644 test/MemberProxy/CMakeLists.txt create mode 100644 test/MemberProxy/add.cpp create mode 100644 test/MemberProxy/set.cpp create mode 100644 test/MemberProxy/subscript.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 43e37638..37e78caa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,12 @@ HEAD * Added `JsonDocument::operator[]` * Added `ARDUINOJSON_TAB` to configure the indentation character * 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()` is not required anymore. +* `JsonDocument` now support the same operations as `JsonVariant`. + Calling `JsonDocument::as()` is not required anymore. +* Fixed example `JsonHttpClient.ino` > ### BREAKING CHANGES > diff --git a/examples/JsonConfigFile/JsonConfigFile.ino b/examples/JsonConfigFile/JsonConfigFile.ino index 5778527f..1b74947a 100644 --- a/examples/JsonConfigFile/JsonConfigFile.ino +++ b/examples/JsonConfigFile/JsonConfigFile.ino @@ -15,7 +15,11 @@ #include #include -// 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 { char hostname[64]; int port; @@ -29,9 +33,9 @@ void loadConfiguration(const char *filename, Config &config) { // Open file for reading 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. - // Use arduinojson.org/assistant to compute the capacity. + // Use arduinojson.org/v6/assistant to compute the capacity. StaticJsonDocument<512> doc; // Deserialize the JSON document @@ -39,16 +43,13 @@ void loadConfiguration(const char *filename, Config &config) { if (error) Serial.println(F("Failed to read file, using default configuration")); - // Get the root object in the document - JsonObject root = doc.as(); + // Copy values from the JsonDocument to the Config + config.port = doc["port"] | 2731; + strlcpy(config.hostname, // <- destination + doc["hostname"] | "example.com", // <- source + sizeof(config.hostname)); // <- destination's capacity - // Copy values from the JsonObject to the Config - config.port = root["port"] | 2731; - strlcpy(config.hostname, // <- destination - root["hostname"] | "example.com", // <- source - 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(); } @@ -64,24 +65,21 @@ void saveConfiguration(const char *filename, const Config &config) { return; } - // Allocate the document on the stack. + // Allocate a temporary JsonDocument // Don't forget to change the capacity to match your requirements. // Use arduinojson.org/assistant to compute the capacity. StaticJsonDocument<256> doc; - // Make our document contain an object - JsonObject root = doc.to(); - - // Set the values in the object - root["hostname"] = config.hostname; - root["port"] = config.port; + // Set the values in the document + doc["hostname"] = config.hostname; + doc["port"] = config.port; // Serialize JSON to file if (serializeJson(doc, file) == 0) { Serial.println(F("Failed to write to file")); } - // Close the file (File's destructor doesn't close the file) + // Close the file file.close(); } @@ -100,7 +98,7 @@ void printFile(const char *filename) { } Serial.println(); - // Close the file (File's destructor doesn't close the file) + // Close the file file.close(); } diff --git a/examples/JsonGeneratorExample/JsonGeneratorExample.ino b/examples/JsonGeneratorExample/JsonGeneratorExample.ino index 24362f6a..2653f148 100644 --- a/examples/JsonGeneratorExample/JsonGeneratorExample.ino +++ b/examples/JsonGeneratorExample/JsonGeneratorExample.ino @@ -15,7 +15,7 @@ void setup() { // // Inside the brackets, 200 is the RAM allocated to this document. // 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; // StaticJsonObject allocates memory on the stack, it can be @@ -23,30 +23,30 @@ void setup() { // // DynamicJsonDocument doc(200); - // Make our document be an object - JsonObject root = doc.to(); - - // Add values in the object + // Add values in the document // - // Most of the time, you can rely on the implicit casts. - // In other case, you can do root.set("time", 1351824120); - root["sensor"] = "gps"; - root["time"] = 1351824120; + doc["sensor"] = "gps"; + doc["time"] = 1351824120; // Add an array. // - JsonArray data = root.createNestedArray("data"); + JsonArray data = doc.createNestedArray("data"); data.add(48.756080); data.add(2.302038); + // Generate the minified JSON and send it to the Serial port. + // serializeJson(doc, Serial); - // This prints: + // The above line prints: // {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]} + // Start a new line Serial.println(); + // Generate the prettified JSON and send it to the Serial port. + // serializeJsonPretty(doc, Serial); - // This prints: + // The above line prints: // { // "sensor": "gps", // "time": 1351824120, diff --git a/examples/JsonHttpClient/JsonHttpClient.ino b/examples/JsonHttpClient/JsonHttpClient.ino index 5bf0f2c1..305cc767 100644 --- a/examples/JsonHttpClient/JsonHttpClient.ino +++ b/examples/JsonHttpClient/JsonHttpClient.ino @@ -71,7 +71,7 @@ void setup() { } // 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; DynamicJsonDocument doc(capacity); @@ -84,12 +84,11 @@ void setup() { } // Extract values - JsonObject root = doc.as(); Serial.println(F("Response:")); - Serial.println(root["sensor"].as()); - Serial.println(root["time"].as()); - Serial.println(root["data"][0].as()); - Serial.println(root["data"][1].as()); + Serial.println(doc["sensor"].as()); + Serial.println(doc["time"].as()); + Serial.println(doc["data"][0].as(), 6); + Serial.println(doc["data"][1].as(), 6); // Disconnect client.stop(); diff --git a/examples/JsonParserExample/JsonParserExample.ino b/examples/JsonParserExample/JsonParserExample.ino index efbe7499..4b578bdf 100644 --- a/examples/JsonParserExample/JsonParserExample.ino +++ b/examples/JsonParserExample/JsonParserExample.ino @@ -13,9 +13,9 @@ void setup() { // 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. - // Use arduinojson.org/assistant to compute the capacity. + // Use arduinojson.org/v6/assistant to compute the capacity. StaticJsonDocument<200> doc; // StaticJsonDocument allocates memory on the stack, it can be @@ -25,9 +25,12 @@ void setup() { // JSON input string. // - // It's better to use a char[] as shown here. - // If you use a const char* or a String, ArduinoJson will - // have to make a copy of the input in the JsonBuffer. + // Using a char[], as shown here, enables the "zero-copy" mode. This mode uses + // the minimal amount of memory because the JsonDocument stores pointers to + // 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[] = "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}"; @@ -41,17 +44,14 @@ void setup() { return; } - // Get the root object in the document - JsonObject root = doc.as(); - // Fetch values. // // Most of the time, you can rely on the implicit casts. - // In other case, you can do root["time"].as(); - const char* sensor = root["sensor"]; - long time = root["time"]; - double latitude = root["data"][0]; - double longitude = root["data"][1]; + // In other case, you can do doc["time"].as(); + const char* sensor = doc["sensor"]; + long time = doc["time"]; + double latitude = doc["data"][0]; + double longitude = doc["data"][1]; // Print values. Serial.println(sensor); diff --git a/examples/JsonServer/JsonServer.ino b/examples/JsonServer/JsonServer.ino index 8356044d..94763d86 100644 --- a/examples/JsonServer/JsonServer.ino +++ b/examples/JsonServer/JsonServer.ino @@ -2,15 +2,15 @@ // Copyright Benoit Blanchon 2014-2018 // MIT License // -// This example shows how to implement an HTTP server that sends JSON document -// in the responses. +// This example shows how to implement an HTTP server that sends a JSON document +// in the response. // 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 looks like the following: +// The JSON document contains the values of the analog and digital pins. +// It looks like that: // { -// "analog": [ 0, 1, 2, 3, 4, 5 ], -// "digital": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 ] +// "analog": [0, 76, 123, 158, 192, 205], +// "digital": [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0] // } #include @@ -51,15 +51,12 @@ void loop() { // Read the request (we ignore the content in this example) while (client.available()) client.read(); - // Allocate the JSON document - // Use arduinojson.org/assistant to compute the capacity. + // Allocate a temporary JsonDocument + // Use arduinojson.org/v6/assistant to compute the capacity. StaticJsonDocument<500> doc; - // Make our document represent an object - JsonObject root = doc.to(); - // Create the "analog" array - JsonArray analogValues = root.createNestedArray("analog"); + JsonArray analogValues = doc.createNestedArray("analog"); for (int pin = 0; pin < 6; pin++) { // Read the analog input int value = analogRead(pin); @@ -69,7 +66,7 @@ void loop() { } // Create the "digital" array - JsonArray digitalValues = root.createNestedArray("digital"); + JsonArray digitalValues = doc.createNestedArray("digital"); for (int pin = 0; pin < 14; pin++) { // Read the digital input int value = digitalRead(pin); @@ -83,9 +80,11 @@ void loop() { Serial.println(); // Write response headers - client.println("HTTP/1.0 200 OK"); - client.println("Content-Type: application/json"); - client.println("Connection: close"); + client.println(F("HTTP/1.0 200 OK")); + client.println(F("Content-Type: application/json")); + client.println(F("Connection: close")); + client.print(F("Content-Length: ")); + client.println(measureJsonPretty(doc)); client.println(); // Write JSON document diff --git a/examples/JsonUdpBeacon/JsonUdpBeacon.ino b/examples/JsonUdpBeacon/JsonUdpBeacon.ino index c636b795..1d2f99b3 100644 --- a/examples/JsonUdpBeacon/JsonUdpBeacon.ino +++ b/examples/JsonUdpBeacon/JsonUdpBeacon.ino @@ -5,10 +5,10 @@ // 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 // analog and digital pins. -// The JSON document looks like the following: +// It looks like that: // { -// "analog": [ 0, 1, 2, 3, 4, 5 ], -// "digital": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 ] +// "analog": [0, 76, 123, 158, 192, 205], +// "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 @@ -43,15 +43,12 @@ void setup() { } void loop() { - // Allocate the JSON document - // Use arduinojson.org/assistant to compute the capacity. + // Allocate a temporary JsonDocument + // Use arduinojson.org/v6/assistant to compute the capacity. StaticJsonDocument<500> doc; - // Make our document represent an object - JsonObject root = doc.to(); - // Create the "analog" array - JsonArray analogValues = root.createNestedArray("analog"); + JsonArray analogValues = doc.createNestedArray("analog"); for (int pin = 0; pin < 6; pin++) { // Read the analog input int value = analogRead(pin); @@ -61,7 +58,7 @@ void loop() { } // Create the "digital" array - JsonArray digitalValues = root.createNestedArray("digital"); + JsonArray digitalValues = doc.createNestedArray("digital"); for (int pin = 0; pin < 14; pin++) { // Read the digital input int value = digitalRead(pin); diff --git a/examples/MsgPackParser/MsgPackParser.ino b/examples/MsgPackParser/MsgPackParser.ino index e1bdc918..9f9a7749 100644 --- a/examples/MsgPackParser/MsgPackParser.ino +++ b/examples/MsgPackParser/MsgPackParser.ino @@ -14,9 +14,9 @@ void setup() { // 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. - // Use arduinojson.org/assistant to compute the capacity. + // Use arduinojson.org/v6/assistant to compute the capacity. StaticJsonDocument<200> doc; // StaticJsonObject allocates memory on the stack, it can be @@ -26,9 +26,12 @@ void setup() { // MessagePack input string. // - // It's better to use a char[] as shown here. - // If you use a const char* or a String, ArduinoJson will - // have to make a copy of the input in the JsonBuffer. + // Using a char[], as shown here, enables the "zero-copy" mode. This mode uses + // the minimal amount of memory because the JsonDocument stores pointers to + // 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, 164, 116, 105, 109, 101, 206, 80, 147, 50, 248, 164, 100, 97, 116, 97, 146, 203, 64, 72, 96, 199, 58, 188, 148, @@ -40,31 +43,23 @@ void setup() { // "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); - // Test if parsing succeeds. + // Test if parsing succeeded. if (error) { Serial.print("deserializeMsgPack() failed: "); Serial.println(error.c_str()); return; } - // Get the root object in the document - JsonObject root = doc.as(); - // Fetch values. // // Most of the time, you can rely on the implicit casts. - // In other case, you can do root["time"].as(); - const char* sensor = root["sensor"]; - long time = root["time"]; - double latitude = root["data"][0]; - double longitude = root["data"][1]; + // In other case, you can do doc["time"].as(); + const char* sensor = doc["sensor"]; + long time = doc["time"]; + double latitude = doc["data"][0]; + double longitude = doc["data"][1]; // Print values. Serial.println(sensor); diff --git a/examples/ProgmemExample/ProgmemExample.ino b/examples/ProgmemExample/ProgmemExample.ino index 44e6a0e2..4de5347a 100644 --- a/examples/ProgmemExample/ProgmemExample.ino +++ b/examples/ProgmemExample/ProgmemExample.ino @@ -6,7 +6,7 @@ // ArduinoJson. // // 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. #include @@ -17,8 +17,7 @@ void setup() { DynamicJsonDocument doc(1024); // You can use a Flash String as your JSON input. - // WARNING: the content of the Flash String will be duplicated in the - // JsonBuffer. + // WARNING: the string in the input will be duplicated in the JsonDocument. deserializeJson(doc, F("{\"sensor\":\"gps\",\"time\":1351824120," "\"data\":[48.756080,2.302038]}")); JsonObject obj = doc.as(); @@ -29,12 +28,12 @@ void setup() { // 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 - // JsonBuffer. + // JsonDocument. obj[F("time")] = time; // You can set a Flash String to a JsonObject or JsonArray: // WARNING: the content of the Flash String will be duplicated in the - // JsonBuffer. + // JsonDocument. obj["sensor"] = F("gps"); // It works with serialized() too: diff --git a/examples/StringExample/StringExample.ino b/examples/StringExample/StringExample.ino index 9c1ec70c..b4cd3297 100644 --- a/examples/StringExample/StringExample.ino +++ b/examples/StringExample/StringExample.ino @@ -5,7 +5,7 @@ // This example shows the different ways you can use String with ArduinoJson. // // 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. #include @@ -14,7 +14,7 @@ void setup() { DynamicJsonDocument doc(1024); // 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 = "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}"; deserializeJson(doc, input); @@ -25,11 +25,11 @@ void setup() { long time = obj[String("time")]; // 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; // 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"]; // Unfortunately, the following doesn't work (issue #118): @@ -38,14 +38,14 @@ void setup() { sensor = obj["sensor"].as(); // 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; // It works with serialized() too: obj["sensor"] = serialized(sensor); // 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"; // You can compare the content of a JsonObject with a String diff --git a/src/ArduinoJson.hpp b/src/ArduinoJson.hpp index a0a6ffee..b056ea73 100644 --- a/src/ArduinoJson.hpp +++ b/src/ArduinoJson.hpp @@ -14,10 +14,10 @@ #include "ArduinoJson/Document/StaticJsonDocument.hpp" #include "ArduinoJson/Array/ArrayImpl.hpp" -#include "ArduinoJson/Array/ArraySubscript.hpp" +#include "ArduinoJson/Array/ElementProxy.hpp" #include "ArduinoJson/Collection/CollectionImpl.hpp" +#include "ArduinoJson/Object/MemberProxy.hpp" #include "ArduinoJson/Object/ObjectImpl.hpp" -#include "ArduinoJson/Object/ObjectSubscript.hpp" #include "ArduinoJson/Variant/VariantAsImpl.hpp" #include "ArduinoJson/Variant/VariantImpl.hpp" diff --git a/src/ArduinoJson/Array/ArrayImpl.hpp b/src/ArduinoJson/Array/ArrayImpl.hpp index c4fab360..cc149f29 100644 --- a/src/ArduinoJson/Array/ArrayImpl.hpp +++ b/src/ArduinoJson/Array/ArrayImpl.hpp @@ -9,11 +9,14 @@ namespace ARDUINOJSON_NAMESPACE { -inline ArrayRef ArrayRef::createNestedArray() const { - return add().to(); +template +inline ArrayRef ArrayShortcuts::createNestedArray() const { + return impl()->add().template to(); } -inline ObjectRef ArrayRef::createNestedObject() const { - return add().to(); +template +inline ObjectRef ArrayShortcuts::createNestedObject() const { + return impl()->add().template to(); } + } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Array/ArrayRef.hpp b/src/ArduinoJson/Array/ArrayRef.hpp index 1e237a99..7d1e06b4 100644 --- a/src/ArduinoJson/Array/ArrayRef.hpp +++ b/src/ArduinoJson/Array/ArrayRef.hpp @@ -16,11 +16,16 @@ namespace ARDUINOJSON_NAMESPACE { class ObjectRef; -class ArraySubscript; +template +class ElementProxy; template class ArrayRefBase { public: + operator VariantConstRef() const { + return VariantConstRef(reinterpret_cast(_data)); + } + template FORCE_INLINE void accept(Visitor& visitor) const { arrayAccept(_data, visitor); @@ -30,10 +35,6 @@ class ArrayRefBase { return _data == 0; } - FORCE_INLINE VariantConstRef operator[](size_t index) const { - return VariantConstRef(_data ? _data->get(index) : 0); - } - FORCE_INLINE size_t memoryUsage() const { return _data ? _data->memoryUsage() : 0; } @@ -74,9 +75,15 @@ class ArrayConstRef : public ArrayRefBase, FORCE_INLINE bool operator==(ArrayConstRef rhs) const { return arrayEquals(_data, rhs._data); } + + FORCE_INLINE VariantConstRef operator[](size_t index) const { + return VariantConstRef(_data ? _data->get(index) : 0); + } }; -class ArrayRef : public ArrayRefBase, public Visitable { +class ArrayRef : public ArrayRefBase, + public ArrayShortcuts, + public Visitable { typedef ArrayRefBase base_type; public: @@ -94,27 +101,7 @@ class ArrayRef : public ArrayRefBase, public Visitable { return ArrayConstRef(_data); } - // 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 - 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 - FORCE_INLINE bool add(T* value) const { - return add().set(value); - } - + using ArrayShortcuts::add; VariantRef add() const { return VariantRef(_pool, arrayAdd(_data, _pool)); } @@ -187,11 +174,6 @@ class ArrayRef : public ArrayRefBase, 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 { return arrayEquals(_data, rhs._data); } diff --git a/src/ArduinoJson/Array/ArrayShortcuts.hpp b/src/ArduinoJson/Array/ArrayShortcuts.hpp new file mode 100644 index 00000000..c2fcbc4d --- /dev/null +++ b/src/ArduinoJson/Array/ArrayShortcuts.hpp @@ -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 +class ElementProxy; + +template +class ArrayShortcuts { + public: + // Returns the element at specified index if the variant is an array. + FORCE_INLINE ElementProxy 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 + FORCE_INLINE bool add(const T &value) const { + return impl()->add().set(value); + } + // + // bool add(TValue); + // TValue = char*, const char*, const __FlashStringHelper* + template + FORCE_INLINE bool add(T *value) const { + return impl()->add().set(value); + } + + private: + const TArray *impl() const { + return static_cast(this); + } +}; +} // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Array/ArraySubscript.hpp b/src/ArduinoJson/Array/ElementProxy.hpp similarity index 51% rename from src/ArduinoJson/Array/ArraySubscript.hpp rename to src/ArduinoJson/Array/ElementProxy.hpp index 2819dfc4..26128571 100644 --- a/src/ArduinoJson/Array/ArraySubscript.hpp +++ b/src/ArduinoJson/Array/ElementProxy.hpp @@ -13,14 +13,18 @@ #endif namespace ARDUINOJSON_NAMESPACE { -class ArraySubscript : public VariantOperators, - public Visitable { + +template +class ElementProxy : public VariantOperators >, + public Visitable { + typedef ElementProxy this_type; + public: - FORCE_INLINE ArraySubscript(ArrayRef array, size_t index) + FORCE_INLINE ElementProxy(TArray array, size_t index) : _array(array), _index(index) {} - FORCE_INLINE ArraySubscript& operator=(const ArraySubscript& src) { - get_impl().set(src.as()); + FORCE_INLINE this_type& operator=(const this_type& src) { + getElement().set(src.as()); return *this; } @@ -30,36 +34,36 @@ class ArraySubscript : public VariantOperators, // TValue = bool, long, int, short, float, double, serialized, VariantRef, // std::string, String, ArrayRef, ObjectRef template - FORCE_INLINE ArraySubscript& operator=(const T& src) { - get_impl().set(src); + FORCE_INLINE this_type& operator=(const T& src) { + getElement().set(src); return *this; } // // operator=(TValue) // TValue = char*, const char*, const __FlashStringHelper* template - FORCE_INLINE ArraySubscript& operator=(T* src) { - get_impl().set(src); + FORCE_INLINE this_type& operator=(T* src) { + getElement().set(src); return *this; } FORCE_INLINE bool isNull() const { - return get_impl().isNull(); + return getElement().isNull(); } template FORCE_INLINE typename VariantAs::type as() const { - return get_impl().as(); + return getElement().template as(); } template FORCE_INLINE bool is() const { - return get_impl().is(); + return getElement().template is(); } template FORCE_INLINE typename VariantTo::type to() const { - return get_impl().to(); + return getElement().template to(); } // Replaces the value @@ -69,42 +73,65 @@ class ArraySubscript : public VariantOperators, // std::string, String, ArrayRef, ObjectRef template FORCE_INLINE bool set(const TValue& value) const { - return get_impl().set(value); + return getElement().set(value); } // // bool set(TValue) // TValue = char*, const char*, const __FlashStringHelper* template FORCE_INLINE bool set(TValue* value) const { - return get_impl().set(value); + return getElement().set(value); } template void accept(Visitor& visitor) const { - return get_impl().accept(visitor); + return getElement().accept(visitor); } FORCE_INLINE size_t size() const { - return get_impl().size(); + return getElement().size(); + } + + template + VariantRef get(TNestedKey* key) const { + return getElement().get(key); + } + + template + VariantRef get(const TNestedKey& key) const { + return getElement().get(key); + } + + template + VariantRef getOrCreate(TNestedKey* key) const { + return getElement().getOrCreate(key); + } + + template + VariantRef getOrCreate(const TNestedKey& key) const { + return getElement().getOrCreate(key); + } + + using ArrayShortcuts::add; + VariantRef add() const { + return getElement().add(); } private: - FORCE_INLINE VariantRef get_impl() const { + FORCE_INLINE VariantRef getElement() const { return _array.get(_index); } - ArrayRef _array; + TArray _array; const size_t _index; }; -template -inline ArraySubscript VariantSubscripts::operator[](size_t index) const { - return impl()->template as()[index]; +template +inline ElementProxy ArrayShortcuts::operator[]( + size_t index) const { + return ElementProxy(*impl(), index); } -inline ArraySubscript ArrayRef::operator[](size_t index) const { - return ArraySubscript(*this, index); -} } // namespace ARDUINOJSON_NAMESPACE #ifdef _MSC_VER diff --git a/src/ArduinoJson/Document/JsonDocument.hpp b/src/ArduinoJson/Document/JsonDocument.hpp index e386e775..433adcd9 100644 --- a/src/ArduinoJson/Document/JsonDocument.hpp +++ b/src/ArduinoJson/Document/JsonDocument.hpp @@ -9,7 +9,8 @@ #include "../Variant/VariantRef.hpp" #include "../Variant/VariantTo.hpp" -#include "../Array/ArraySubscript.hpp" +#include "../Array/ElementProxy.hpp" +#include "../Object/MemberProxy.hpp" namespace ARDUINOJSON_NAMESPACE { @@ -81,22 +82,51 @@ class JsonDocument : public Visitable { return _data; } - // ObjectSubscript operator[](TKey) - // TKey = const std::string&, const String& - template - FORCE_INLINE typename enable_if::value, - ObjectSubscript >::type - operator[](const TKey& key) { - return getVariant()[key]; + ArrayRef createNestedArray() { + return add().to(); } - // ObjectSubscript operator[](TKey); - // TKey = const char*, const char[N], const __FlashStringHelper* + template + ArrayRef createNestedArray(TKey* key) { + return getOrCreate(key).template to(); + } + + template + ArrayRef createNestedArray(const TKey& key) { + return getOrCreate(key).template to(); + } + + ObjectRef createNestedObject() { + return add().to(); + } + + template + ObjectRef createNestedObject(TKey* key) { + return getOrCreate(key).template to(); + } + + template + ObjectRef createNestedObject(const TKey& key) { + return getOrCreate(key).template to(); + } + + // MemberProxy operator[](TKey) + // TKey = const std::string&, const String& template FORCE_INLINE - typename enable_if::value, ObjectSubscript >::type - operator[](TKey* key) { - return getVariant()[key]; + typename enable_if::value, + MemberProxy >::type + operator[](const TKey& key) { + return MemberProxy(*this, key); + } + + // MemberProxy operator[](TKey); + // TKey = const char*, const char[N], const __FlashStringHelper* + template + FORCE_INLINE typename enable_if::value, + MemberProxy >::type + operator[](TKey* key) { + return MemberProxy(*this, key); } // VariantConstRef operator[](TKey) const @@ -115,12 +145,56 @@ class JsonDocument : public Visitable { return getVariant()[key]; } - FORCE_INLINE ArraySubscript operator[](size_t index) { - return getVariant()[index]; + FORCE_INLINE ElementProxy operator[](size_t index) { + return ElementProxy(*this, index); } 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 + FORCE_INLINE VariantRef get(TKey* key) { + return VariantRef(&_pool, _data.get(wrapString(key))); + } + + template + FORCE_INLINE typename enable_if::value, VariantRef>::type get( + const TKey& key) { + return VariantRef(&_pool, _data.get(wrapString(key))); + } + + template + FORCE_INLINE VariantRef getOrCreate(TKey* key) { + return VariantRef(&_pool, _data.getOrCreate(wrapString(key), &_pool)); + } + + template + 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 + FORCE_INLINE bool add(const T& value) { + return add().set(value); + } + // + // bool add(TValue); + // TValue = char*, const char*, const __FlashStringHelper* + template + FORCE_INLINE bool add(T* value) { + return add().set(value); } protected: diff --git a/src/ArduinoJson/Misc/Visitable.hpp b/src/ArduinoJson/Misc/Visitable.hpp index 58437692..6d27c252 100644 --- a/src/ArduinoJson/Misc/Visitable.hpp +++ b/src/ArduinoJson/Misc/Visitable.hpp @@ -15,4 +15,7 @@ struct Visitable { template struct IsVisitable : is_base_of {}; + +template +struct IsVisitable : IsVisitable {}; } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Object/ObjectSubscript.hpp b/src/ArduinoJson/Object/MemberProxy.hpp similarity index 50% rename from src/ArduinoJson/Object/ObjectSubscript.hpp rename to src/ArduinoJson/Object/MemberProxy.hpp index 449dce6d..68db8234 100644 --- a/src/ArduinoJson/Object/ObjectSubscript.hpp +++ b/src/ArduinoJson/Object/MemberProxy.hpp @@ -15,21 +15,21 @@ namespace ARDUINOJSON_NAMESPACE { -template -class ObjectSubscript : public VariantOperators >, - public Visitable { - typedef ObjectSubscript this_type; +template +class MemberProxy : public VariantOperators >, + public Visitable { + typedef MemberProxy this_type; public: - FORCE_INLINE ObjectSubscript(ObjectRef object, TStringRef key) - : _object(object), _key(key) {} + FORCE_INLINE MemberProxy(TObject variant, TString key) + : _object(variant), _key(key) {} - operator VariantConstRef() const { - return get_impl(); + FORCE_INLINE operator VariantConstRef() const { + return getMember(); } FORCE_INLINE this_type &operator=(const this_type &src) { - set_impl().set(src); + getOrCreateMember().set(src); return *this; } @@ -41,7 +41,7 @@ class ObjectSubscript : public VariantOperators >, template FORCE_INLINE typename enable_if::value, this_type &>::type operator=(const TValue &src) { - set_impl().set(src); + getOrCreateMember().set(src); return *this; } // @@ -49,27 +49,27 @@ class ObjectSubscript : public VariantOperators >, // TValue = char*, const char*, const __FlashStringHelper* template FORCE_INLINE this_type &operator=(TValue *src) { - set_impl().set(src); + getOrCreateMember().set(src); return *this; } FORCE_INLINE bool isNull() const { - return get_impl().isNull(); + return getMember().isNull(); } template FORCE_INLINE typename VariantAs::type as() const { - return get_impl().template as(); + return getMember().template as(); } template FORCE_INLINE bool is() const { - return get_impl().template is(); + return getMember().template is(); } template FORCE_INLINE typename VariantTo::type to() { - return set_impl().template to(); + return getOrCreateMember().template to(); } // Sets the specified value. @@ -81,48 +81,73 @@ class ObjectSubscript : public VariantOperators >, template FORCE_INLINE typename enable_if::value, bool>::type set( const TValue &value) { - return set_impl().set(value); + return getOrCreateMember().set(value); } // // bool set(TValue); // TValue = char*, const char, const __FlashStringHelper* template FORCE_INLINE bool set(const TValue *value) { - return set_impl().set(value); + return getOrCreateMember().set(value); } template void accept(Visitor &visitor) const { - return get_impl().accept(visitor); + return getMember().accept(visitor); + } + + using ArrayShortcuts::add; + FORCE_INLINE VariantRef add() const { + return getOrCreateMember().add(); + } + + template + FORCE_INLINE VariantRef get(TNestedKey *key) const { + return getMember().get(key); + } + + template + FORCE_INLINE VariantRef get(const TNestedKey &key) const { + return getMember().get(key); + } + + template + FORCE_INLINE VariantRef getOrCreate(TNestedKey *key) const { + return getOrCreateMember().getOrCreate(key); + } + + template + FORCE_INLINE VariantRef getOrCreate(const TNestedKey &key) const { + return getOrCreateMember().getOrCreate(key); } private: - FORCE_INLINE VariantRef get_impl() const { + FORCE_INLINE VariantRef getMember() const { return _object.get(_key); } - FORCE_INLINE VariantRef set_impl() const { - return _object.set(_key); + FORCE_INLINE VariantRef getOrCreateMember() const { + return _object.getOrCreate(_key); } - ObjectRef _object; - TStringRef _key; + TObject _object; + TString _key; }; -template +template template inline typename enable_if::value, - ObjectSubscript >::type - VariantSubscripts::operator[](const TString &key) const { - return impl()->template as()[key]; + MemberProxy >::type + ObjectShortcuts::operator[](const TString &key) const { + return MemberProxy(*impl(), key); } -template +template template inline typename enable_if::value, - ObjectSubscript >::type - VariantSubscripts::operator[](TString *key) const { - return impl()->template as()[key]; + MemberProxy >::type + ObjectShortcuts::operator[](TString *key) const { + return MemberProxy(*impl(), key); } } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Object/ObjectFunctions.hpp b/src/ArduinoJson/Object/ObjectFunctions.hpp index abdebe24..20b5bbdd 100644 --- a/src/ArduinoJson/Object/ObjectFunctions.hpp +++ b/src/ArduinoJson/Object/ObjectFunctions.hpp @@ -40,7 +40,8 @@ void objectRemove(CollectionData *obj, TKey key) { } template -inline VariantData *objectSet(CollectionData *obj, TKey key, MemoryPool *pool) { +inline VariantData *objectGetOrCreate(CollectionData *obj, TKey key, + MemoryPool *pool) { if (!obj) return 0; // ignore null key diff --git a/src/ArduinoJson/Object/ObjectImpl.hpp b/src/ArduinoJson/Object/ObjectImpl.hpp index 791b90eb..10523ab9 100644 --- a/src/ArduinoJson/Object/ObjectImpl.hpp +++ b/src/ArduinoJson/Object/ObjectImpl.hpp @@ -9,13 +9,31 @@ namespace ARDUINOJSON_NAMESPACE { +template template -inline ArrayRef ObjectRef::createNestedArray(const TString& key) const { - return set(key).template to(); +inline ArrayRef ObjectShortcuts::createNestedArray( + const TString& key) const { + return impl()->getOrCreate(key).template to(); } +template template -inline ArrayRef ObjectRef::createNestedArray(TString* key) const { - return set(key).template to(); +inline ArrayRef ObjectShortcuts::createNestedArray( + TString* key) const { + return impl()->getOrCreate(key).template to(); +} + +template +template +ObjectRef ObjectShortcuts::createNestedObject(const TKey& key) const { + return impl()->getOrCreate(key).template to(); +} +// +// ObjectRef createNestedObject(TKey); +// TKey = char*, const char*, char[], const char[], const __FlashStringHelper* +template +template +ObjectRef ObjectShortcuts::createNestedObject(TKey* key) const { + return impl()->getOrCreate(key).template to(); } } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Object/ObjectRef.hpp b/src/ArduinoJson/Object/ObjectRef.hpp index 208a2430..26e82ba0 100644 --- a/src/ArduinoJson/Object/ObjectRef.hpp +++ b/src/ArduinoJson/Object/ObjectRef.hpp @@ -17,6 +17,10 @@ namespace ARDUINOJSON_NAMESPACE { template class ObjectRefBase { public: + operator VariantConstRef() const { + return VariantConstRef(reinterpret_cast(_data)); + } + template FORCE_INLINE void accept(Visitor& visitor) const { objectAccept(_data, visitor); @@ -127,7 +131,9 @@ class ObjectConstRef : public ObjectRefBase, } }; -class ObjectRef : public ObjectRefBase, public Visitable { +class ObjectRef : public ObjectRefBase, + public ObjectShortcuts, + public Visitable { typedef ObjectRefBase base_type; public: @@ -164,68 +170,30 @@ class ObjectRef : public ObjectRefBase, public Visitable { return _data->copyFrom(*src._data, _pool); } - // Creates and adds a ArrayRef. - // - // ArrayRef createNestedArray(TKey); - // TKey = const std::string&, const String& - template - FORCE_INLINE ArrayRef createNestedArray(const TKey& key) const; - // ArrayRef createNestedArray(TKey); - // TKey = char*, const char*, char[], const char[], const __FlashStringHelper* - template - FORCE_INLINE ArrayRef createNestedArray(TKey* key) const; - - // Creates and adds a ObjectRef. - // - // ObjectRef createNestedObject(TKey); - // TKey = const std::string&, const String& - template - FORCE_INLINE ObjectRef createNestedObject(const TKey& key) const { - return set(key).template to(); - } - // - // ObjectRef createNestedObject(TKey); - // TKey = char*, const char*, char[], const char[], const __FlashStringHelper* - template - FORCE_INLINE ObjectRef createNestedObject(TKey* key) const { - return set(key).template to(); - } - // Gets the value associated with the specified key. // - // TValue get(TKey) const; + // VariantRef get(TKey) const; // TKey = const std::string&, const String& - // TValue = bool, char, long, int, short, float, double, - // std::string, String, ArrayRef, ObjectRef template FORCE_INLINE VariantRef get(const TKey& key) const { return get_impl(wrapString(key)); } // - // TValue get(TKey) const; + // VariantRef get(TKey) const; // TKey = char*, const char*, const __FlashStringHelper* - // TValue = bool, char, long, int, short, float, double, - // std::string, String, ArrayRef, ObjectRef template FORCE_INLINE VariantRef get(TKey* key) const { 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 - FORCE_INLINE ObjectSubscript operator[](const TKey& key) const { - return ObjectSubscript(*this, key); + FORCE_INLINE VariantRef getOrCreate(TKey* key) const { + return getOrCreate_impl(wrapString(key)); } - // - // ObjectSubscript operator[](TKey) - // TKey = char*, const char*, char[], const char[N], const - // __FlashStringHelper* + template - FORCE_INLINE ObjectSubscript operator[](TKey* key) const { - return ObjectSubscript(*this, key); + FORCE_INLINE VariantRef getOrCreate(const TKey& key) const { + return getOrCreate_impl(wrapString(key)); } FORCE_INLINE bool operator==(ObjectRef rhs) const { @@ -253,16 +221,6 @@ class ObjectRef : public ObjectRefBase, public Visitable { objectRemove(_data, wrapString(key)); } - template - FORCE_INLINE VariantRef set(TKey* key) const { - return set_impl(wrapString(key)); - } - - template - FORCE_INLINE VariantRef set(const TKey& key) const { - return set_impl(wrapString(key)); - } - private: template FORCE_INLINE VariantRef get_impl(TKey key) const { @@ -270,8 +228,8 @@ class ObjectRef : public ObjectRefBase, public Visitable { } template - FORCE_INLINE VariantRef set_impl(TKey key) const { - return VariantRef(_pool, objectSet(_data, key, _pool)); + FORCE_INLINE VariantRef getOrCreate_impl(TKey key) const { + return VariantRef(_pool, objectGetOrCreate(_data, key, _pool)); } MemoryPool* _pool; diff --git a/src/ArduinoJson/Object/ObjectShortcuts.hpp b/src/ArduinoJson/Object/ObjectShortcuts.hpp new file mode 100644 index 00000000..3cbc088e --- /dev/null +++ b/src/ArduinoJson/Object/ObjectShortcuts.hpp @@ -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 +class MemberProxy; + +template +class ObjectShortcuts { + public: + // MemberProxy operator[](TKey) const; + // TKey = const std::string&, const String& + template + FORCE_INLINE + typename enable_if::value, + MemberProxy >::type + operator[](const TKey &key) const; + // + // MemberProxy operator[](TKey) const; + // TKey = const char*, const char[N], const __FlashStringHelper* + template + FORCE_INLINE typename enable_if::value, + MemberProxy >::type + operator[](TKey *key) const; + + // Creates and adds a ArrayRef. + // + // ArrayRef createNestedArray(TKey); + // TKey = const std::string&, const String& + template + FORCE_INLINE ArrayRef createNestedArray(const TKey &key) const; + // ArrayRef createNestedArray(TKey); + // TKey = char*, const char*, char[], const char[], const __FlashStringHelper* + template + FORCE_INLINE ArrayRef createNestedArray(TKey *key) const; + + // Creates and adds a ObjectRef. + // + // ObjectRef createNestedObject(TKey); + // TKey = const std::string&, const String& + template + ObjectRef createNestedObject(const TKey &key) const; + // + // ObjectRef createNestedObject(TKey); + // TKey = char*, const char*, char[], const char[], const __FlashStringHelper* + template + ObjectRef createNestedObject(TKey *key) const; + + private: + const TObject *impl() const { + return static_cast(this); + } +}; +} // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Operators/VariantOperators.hpp b/src/ArduinoJson/Operators/VariantOperators.hpp index 15adeb42..bc4a839c 100644 --- a/src/ArduinoJson/Operators/VariantOperators.hpp +++ b/src/ArduinoJson/Operators/VariantOperators.hpp @@ -7,7 +7,7 @@ #include "VariantCasts.hpp" #include "VariantComparisons.hpp" #include "VariantOr.hpp" -#include "VariantSubscripts.hpp" +#include "VariantShortcuts.hpp" namespace ARDUINOJSON_NAMESPACE { @@ -15,5 +15,5 @@ template class VariantOperators : public VariantCasts, public VariantComparisons, public VariantOr, - public VariantSubscripts {}; + public VariantShortcuts {}; } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Operators/VariantShortcuts.hpp b/src/ArduinoJson/Operators/VariantShortcuts.hpp new file mode 100644 index 00000000..65e997d5 --- /dev/null +++ b/src/ArduinoJson/Operators/VariantShortcuts.hpp @@ -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 +class VariantShortcuts : public ObjectShortcuts, + public ArrayShortcuts { + public: + using ArrayShortcuts::createNestedArray; + using ArrayShortcuts::createNestedObject; + using ArrayShortcuts::operator[]; + using ObjectShortcuts::createNestedArray; + using ObjectShortcuts::createNestedObject; + using ObjectShortcuts::operator[]; +}; +} // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Operators/VariantSubscripts.hpp b/src/ArduinoJson/Operators/VariantSubscripts.hpp deleted file mode 100644 index ebc7585c..00000000 --- a/src/ArduinoJson/Operators/VariantSubscripts.hpp +++ /dev/null @@ -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 -class ObjectSubscript; - -template -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 - FORCE_INLINE typename enable_if::value, - ObjectSubscript >::type - operator[](const TKey &key) const; - // - // ObjectSubscript operator[](TKey) const; - // TKey = const char*, const char[N], const __FlashStringHelper* - template - FORCE_INLINE typename enable_if::value, - ObjectSubscript >::type - operator[](TKey *key) const; - - private: - const TImpl *impl() const { - return static_cast(this); - } -}; -} // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Variant/VariantContent.hpp b/src/ArduinoJson/Variant/VariantContent.hpp index aac4a538..eb9653a2 100644 --- a/src/ArduinoJson/Variant/VariantContent.hpp +++ b/src/ArduinoJson/Variant/VariantContent.hpp @@ -12,19 +12,24 @@ namespace ARDUINOJSON_NAMESPACE { +// 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_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 }; diff --git a/src/ArduinoJson/Variant/VariantData.hpp b/src/ArduinoJson/Variant/VariantData.hpp index c99309fd..9cdf82bb 100644 --- a/src/ArduinoJson/Variant/VariantData.hpp +++ b/src/ArduinoJson/Variant/VariantData.hpp @@ -67,7 +67,7 @@ class VariantData { } CollectionData *asArray() { - return type() == VALUE_IS_ARRAY ? &_content.asCollection : 0; + return isArray() ? &_content.asCollection : 0; } const CollectionData *asArray() const { @@ -75,7 +75,7 @@ class VariantData { } CollectionData *asObject() { - return type() == VALUE_IS_OBJECT ? &_content.asCollection : 0; + return isObject() ? &_content.asCollection : 0; } const CollectionData *asObject() const { @@ -135,13 +135,17 @@ class VariantData { } bool isArray() const { - return type() == VALUE_IS_ARRAY; + return (_flags & VALUE_IS_ARRAY) != 0; } bool isBoolean() const { return type() == VALUE_IS_BOOLEAN; } + bool isCollection() const { + return (_flags & COLLECTION_MASK) != 0; + } + bool isInteger() const { return type() == VALUE_IS_POSITIVE_INTEGER || type() == VALUE_IS_NEGATIVE_INTEGER; @@ -153,12 +157,11 @@ class VariantData { } bool isString() const { - return (type() == VALUE_IS_LINKED_STRING || - type() == VALUE_IS_OWNED_STRING); + return type() == VALUE_IS_LINKED_STRING || type() == VALUE_IS_OWNED_STRING; } bool isObject() const { - return type() == VALUE_IS_OBJECT; + return (_flags & VALUE_IS_OBJECT) != 0; } bool isNull() const { @@ -275,20 +278,35 @@ class VariantData { } size_t nesting() const { - switch (type()) { - case VALUE_IS_OBJECT: - case VALUE_IS_ARRAY: - return _content.asCollection.nesting(); - default: - return 0; - } + return isCollection() ? _content.asCollection.nesting() : 0; } size_t size() const { - if (type() == VALUE_IS_OBJECT || type() == VALUE_IS_ARRAY) - return _content.asCollection.size(); - else - return 0; + return isCollection() ? _content.asCollection.size() : 0; + } + + VariantData *get(size_t index) const { + return isArray() ? _content.asCollection.get(index) : 0; + } + + template + VariantData *get(TKey key) const { + return isObject() ? _content.asCollection.get(key) : 0; + } + + template + 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: diff --git a/src/ArduinoJson/Variant/VariantFunctions.hpp b/src/ArduinoJson/Variant/VariantFunctions.hpp index d3d0c976..7a6e76f4 100644 --- a/src/ArduinoJson/Variant/VariantFunctions.hpp +++ b/src/ArduinoJson/Variant/VariantFunctions.hpp @@ -146,4 +146,20 @@ inline CollectionData *variantToObject(VariantData *var) { return &var->toObject(); } +inline NO_INLINE VariantData *variantAdd(VariantData *var, MemoryPool *pool) { + return var != 0 ? var->add(pool) : 0; +} + +template +NO_INLINE VariantData *variantGetOrCreate(VariantData *var, TKey *key, + MemoryPool *pool) { + return var != 0 ? var->getOrCreate(wrapString(key), pool) : 0; +} + +template +NO_INLINE VariantData *variantGetOrCreate(VariantData *var, const TKey &key, + MemoryPool *pool) { + return var != 0 ? var->getOrCreate(wrapString(key), pool) : 0; +} + } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Variant/VariantImpl.hpp b/src/ArduinoJson/Variant/VariantImpl.hpp index b61065f5..0b861779 100644 --- a/src/ArduinoJson/Variant/VariantImpl.hpp +++ b/src/ArduinoJson/Variant/VariantImpl.hpp @@ -50,7 +50,7 @@ inline T VariantData::asFloat() const { } } -inline const char* VariantData::asString() const { +inline const char *VariantData::asString() const { switch (type()) { case VALUE_IS_LINKED_STRING: case VALUE_IS_OWNED_STRING: @@ -60,37 +60,11 @@ inline const char* VariantData::asString() const { } } -inline bool VariantRef::set(ArrayRef array) const { - return to().copyFrom(array); -} - -inline bool VariantRef::set(ArrayConstRef array) const { - return to().copyFrom(array); -} - -inline bool VariantRef::set(const ArraySubscript& value) const { - return set(value.as()); -} - -inline bool VariantRef::set(ObjectRef object) const { - return to().copyFrom(object); -} - -inline bool VariantRef::set(ObjectConstRef object) const { - return to().copyFrom(object); -} - -template -inline bool VariantRef::set(const ObjectSubscript& value) const { - return set(value.template as()); -} - -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 enable_if::value, bool>::type VariantRef::set( + const TVariant &value) const { + VariantConstRef v = value; + return variantCopyFrom(_data, v._data, _pool); } template @@ -128,4 +102,32 @@ inline VariantConstRef VariantConstRef::operator[](size_t index) const { 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 +inline VariantRef VariantRef::get(TKey *key) const { + return VariantRef(_pool, _data != 0 ? _data->get(wrapString(key)) : 0); +} + +template +inline typename enable_if::value, VariantRef>::type +VariantRef::get(const TKey &key) const { + return VariantRef(_pool, _data != 0 ? _data->get(wrapString(key)) : 0); +} + +template +inline VariantRef VariantRef::getOrCreate(TKey *key) const { + return VariantRef(_pool, variantGetOrCreate(_data, key, _pool)); +} + +template +inline VariantRef VariantRef::getOrCreate(const TKey &key) const { + return VariantRef(_pool, variantGetOrCreate(_data, key, _pool)); +} } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Variant/VariantRef.hpp b/src/ArduinoJson/Variant/VariantRef.hpp index 31031965..7dfeff62 100644 --- a/src/ArduinoJson/Variant/VariantRef.hpp +++ b/src/ArduinoJson/Variant/VariantRef.hpp @@ -23,6 +23,9 @@ namespace ARDUINOJSON_NAMESPACE { class ArrayRef; class ObjectRef; +template +class MemberProxy; + // Contains the methods shared by VariantRef and VariantConstRef template class VariantRefBase { @@ -207,16 +210,15 @@ class VariantRef : public VariantRefBase, return variantSetLinkedString(_data, value); } - bool set(VariantConstRef value) const; - bool set(VariantRef value) const; - - FORCE_INLINE bool set(ArrayRef array) const; - FORCE_INLINE bool set(ArrayConstRef array) const; - FORCE_INLINE bool set(const ArraySubscript &) const; - FORCE_INLINE bool set(ObjectRef object) const; - FORCE_INLINE bool set(ObjectConstRef object) const; - template - FORCE_INLINE bool set(const ObjectSubscript &) const; + // set(VariantRef) + // set(VariantConstRef) + // set(ArrayRef) + // set(ArrayConstRef) + // set(ObjectRef) + // set(ObjecConstRef) + template + typename enable_if::value, bool>::type set( + const TVariant &value) const; // Get the variant as the specified type. // @@ -278,6 +280,26 @@ class VariantRef : public VariantRefBase, typename enable_if::value, VariantRef>::type to() const; + VariantRef add() const; + using ArrayShortcuts::add; + + FORCE_INLINE VariantRef get(size_t) const; + + template + FORCE_INLINE VariantRef get(TKey *) const; + + // get(const char*) + // get(const __FlashStringHelper*) + template + FORCE_INLINE typename enable_if::value, VariantRef>::type get( + const TKey &) const; + + template + FORCE_INLINE VariantRef getOrCreate(TKey *) const; + + template + FORCE_INLINE VariantRef getOrCreate(const TKey &) const; + private: MemoryPool *_pool; }; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7f578ba8..10d058fb 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -71,6 +71,7 @@ if(MSVC) ) endif() +add_subdirectory(ElementProxy) add_subdirectory(IntegrationTests) add_subdirectory(JsonArray) add_subdirectory(JsonDeserializer) @@ -78,10 +79,11 @@ add_subdirectory(JsonDocument) add_subdirectory(JsonObject) add_subdirectory(JsonSerializer) add_subdirectory(JsonVariant) -add_subdirectory(TextFormatter) +add_subdirectory(MemberProxy) add_subdirectory(MemoryPool) add_subdirectory(Misc) add_subdirectory(MixedConfiguration) add_subdirectory(MsgPackDeserializer) add_subdirectory(MsgPackSerializer) add_subdirectory(Numbers) +add_subdirectory(TextFormatter) diff --git a/test/ElementProxy/CMakeLists.txt b/test/ElementProxy/CMakeLists.txt new file mode 100644 index 00000000..5a732002 --- /dev/null +++ b/test/ElementProxy/CMakeLists.txt @@ -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) diff --git a/test/ElementProxy/add.cpp b/test/ElementProxy/add.cpp new file mode 100644 index 00000000..8f97e9fb --- /dev/null +++ b/test/ElementProxy/add.cpp @@ -0,0 +1,26 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +using namespace ARDUINOJSON_NAMESPACE; + +TEST_CASE("ElementProxy::add()") { + DynamicJsonDocument doc(4096); + doc.add(); + ElementProxy ep = doc[0]; + + SECTION("add(int)") { + ep.add(42); + + REQUIRE(doc.as() == "[[42]]"); + } + + SECTION("add(const char*)") { + ep.add("world"); + + REQUIRE(doc.as() == "[[\"world\"]]"); + } +} diff --git a/test/ElementProxy/set.cpp b/test/ElementProxy/set.cpp new file mode 100644 index 00000000..fe8a9980 --- /dev/null +++ b/test/ElementProxy/set.cpp @@ -0,0 +1,26 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +using namespace ARDUINOJSON_NAMESPACE; + +TEST_CASE("ElementProxy::set()") { + DynamicJsonDocument doc(4096); + doc.add(); + ElementProxy ep = doc[0]; + + SECTION("set(int)") { + ep.set(42); + + REQUIRE(doc.as() == "[42]"); + } + + SECTION("set(const char*)") { + ep.set("world"); + + REQUIRE(doc.as() == "[\"world\"]"); + } +} diff --git a/test/JsonDocument/CMakeLists.txt b/test/JsonDocument/CMakeLists.txt index 9bf27806..e9d3aa66 100644 --- a/test/JsonDocument/CMakeLists.txt +++ b/test/JsonDocument/CMakeLists.txt @@ -3,9 +3,11 @@ # MIT License add_executable(JsonDocumentTests + add.cpp + createNested.cpp DynamicJsonDocument.cpp - nesting.cpp isNull.cpp + nesting.cpp StaticJsonDocument.cpp subscript.cpp ) diff --git a/test/JsonDocument/add.cpp b/test/JsonDocument/add.cpp new file mode 100644 index 00000000..a7957ff2 --- /dev/null +++ b/test/JsonDocument/add.cpp @@ -0,0 +1,22 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +TEST_CASE("JsonDocument::add()") { + DynamicJsonDocument doc(4096); + + SECTION("integer") { + doc.add(42); + + REQUIRE(doc.as() == "[42]"); + } + + SECTION("const char*") { + doc.add("hello"); + + REQUIRE(doc.as() == "[\"hello\"]"); + } +} diff --git a/test/JsonDocument/createNested.cpp b/test/JsonDocument/createNested.cpp new file mode 100644 index 00000000..62e1ccd9 --- /dev/null +++ b/test/JsonDocument/createNested.cpp @@ -0,0 +1,66 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +TEST_CASE("JsonDocument::createNestedArray()") { + DynamicJsonDocument doc(4096); + + SECTION("promotes to array") { + doc.createNestedArray(); + + REQUIRE(doc.is()); + } +} + +TEST_CASE("JsonDocument::createNestedArray(key)") { + DynamicJsonDocument doc(4096); + + SECTION("key is const char*") { + SECTION("promotes to object") { + doc.createNestedArray("hello"); + + REQUIRE(doc.is()); + } + } + + SECTION("key is std::string") { + SECTION("promotes to object") { + doc.createNestedArray(std::string("hello")); + + REQUIRE(doc.is()); + } + } +} + +TEST_CASE("JsonDocument::createNestedObject()") { + DynamicJsonDocument doc(4096); + + SECTION("promotes to array") { + doc.createNestedObject(); + + REQUIRE(doc.is()); + } +} + +TEST_CASE("JsonDocument::createNestedObject(key)") { + DynamicJsonDocument doc(4096); + + SECTION("key is const char*") { + SECTION("promotes to object") { + doc.createNestedObject("hello"); + + REQUIRE(doc.is()); + } + } + + SECTION("key is std::string") { + SECTION("promotes to object") { + doc.createNestedObject(std::string("hello")); + + REQUIRE(doc.is()); + } + } +} diff --git a/test/JsonDocument/subscript.cpp b/test/JsonDocument/subscript.cpp index 29c523c1..ba24470d 100644 --- a/test/JsonDocument/subscript.cpp +++ b/test/JsonDocument/subscript.cpp @@ -21,6 +21,11 @@ TEST_CASE("JsonDocument::operator[]") { REQUIRE(doc[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") { @@ -30,3 +35,11 @@ TEST_CASE("JsonDocument::operator[]") { 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); +} diff --git a/test/JsonSerializer/misc.cpp b/test/JsonSerializer/misc.cpp index 14bd9def..1cf24d09 100644 --- a/test/JsonSerializer/misc.cpp +++ b/test/JsonSerializer/misc.cpp @@ -12,7 +12,7 @@ void check(T value, const std::string &expected) { REQUIRE(expected.size() == returnValue); } -TEST_CASE("serializeJson(JsonObjectSubscript)") { +TEST_CASE("serializeJson(MemberProxy)") { DynamicJsonDocument doc(4096); deserializeJson(doc, "{\"hello\":42}"); JsonObject obj = doc.as(); @@ -23,7 +23,7 @@ TEST_CASE("serializeJson(JsonObjectSubscript)") { REQUIRE(result == "42"); } -TEST_CASE("serializeJson(JsonArraySubscript)") { +TEST_CASE("serializeJson(ElementProxy)") { DynamicJsonDocument doc(4096); deserializeJson(doc, "[42]"); JsonArray arr = doc.as(); diff --git a/test/JsonSerializer/std_stream.cpp b/test/JsonSerializer/std_stream.cpp index 464234ab..cd48c266 100644 --- a/test/JsonSerializer/std_stream.cpp +++ b/test/JsonSerializer/std_stream.cpp @@ -37,7 +37,7 @@ TEST_CASE("operator<<(std::ostream)") { REQUIRE("{\"key\":\"value\"}" == os.str()); } - SECTION("JsonObjectSubscript") { + SECTION("MemberProxy") { JsonObject object = doc.to(); object["key"] = "value"; @@ -55,7 +55,7 @@ TEST_CASE("operator<<(std::ostream)") { REQUIRE("[\"value\"]" == os.str()); } - SECTION("JsonArraySubscript") { + SECTION("ElementProxy") { JsonArray array = doc.to(); array.add("value"); diff --git a/test/JsonVariant/CMakeLists.txt b/test/JsonVariant/CMakeLists.txt index 7e67ae6d..5fb26353 100644 --- a/test/JsonVariant/CMakeLists.txt +++ b/test/JsonVariant/CMakeLists.txt @@ -3,18 +3,21 @@ # MIT License add_executable(JsonVariantTests + add.cpp as.cpp compare.cpp copy.cpp + createNested.cpp get.cpp is.cpp isnull.cpp memoryUsage.cpp - nesting.cpp misc.cpp + nesting.cpp or.cpp set.cpp subscript.cpp + types.cpp undefined.cpp ) diff --git a/test/JsonVariant/add.cpp b/test/JsonVariant/add.cpp new file mode 100644 index 00000000..840985dd --- /dev/null +++ b/test/JsonVariant/add.cpp @@ -0,0 +1,39 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include +#include + +static const char* null = 0; + +TEST_CASE("JsonVariant::add()") { + DynamicJsonDocument doc(4096); + JsonVariant var = doc.to(); + + SECTION("No argument") { + JsonVariant nested = var.add(); + + REQUIRE(var.is() == true); + REQUIRE(nested.isNull() == true); + } + + SECTION("integer") { + var.add(42); + + REQUIRE(var.as() == "[42]"); + } + + SECTION("const char*") { + var.add("hello"); + + REQUIRE(var.as() == "[\"hello\"]"); + } + + SECTION("std::string") { + var.add(std::string("hello")); + + REQUIRE(var.as() == "[\"hello\"]"); + } +} diff --git a/test/JsonVariant/createNested.cpp b/test/JsonVariant/createNested.cpp new file mode 100644 index 00000000..5bea89a7 --- /dev/null +++ b/test/JsonVariant/createNested.cpp @@ -0,0 +1,88 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include +#include + +static const char* null = 0; + +TEST_CASE("JsonVariant::createNestedObject()") { + DynamicJsonDocument doc(4096); + JsonVariant variant = doc.to(); + + SECTION("promotes to array") { + JsonObject obj = variant.createNestedObject(); + obj["value"] = "42"; + + REQUIRE(variant.is() == 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(); + + SECTION("promotes to array") { + JsonArray arr = variant.createNestedArray(); + + REQUIRE(variant.is() == 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(); + + SECTION("promotes to object") { + JsonObject obj = variant.createNestedObject("weather"); + obj["temp"] = "42"; + + REQUIRE(variant.is() == 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(); + + SECTION("promotes to object") { + JsonArray arr = variant.createNestedArray("items"); + + REQUIRE(variant.is() == true); + REQUIRE(arr.isNull() == false); + } + + SECTION("works on MemberProxy") { + JsonArray arr = variant["weather"].createNestedArray("temp"); + arr.add("42"); + + REQUIRE(variant["weather"]["temp"][0] == 42); + } +} diff --git a/test/JsonVariant/get.cpp b/test/JsonVariant/get.cpp index 55214b10..ada2a720 100644 --- a/test/JsonVariant/get.cpp +++ b/test/JsonVariant/get.cpp @@ -3,138 +3,27 @@ // MIT License #include -#include #include -#include -template -void checkValue(T expected) { +TEST_CASE("JsonVariant::get()") { DynamicJsonDocument doc(4096); - JsonVariant variant = doc.to(); + JsonVariant var = doc.to(); - variant.set(expected); - REQUIRE(expected == variant.as()); -} + SECTION("get(const char*)") { + var["value"] = 42; -template -void checkReference(T &expected) { - JsonVariant variant = expected; - REQUIRE(expected == variant.as()); -} - -template -void checkNumericType() { - DynamicJsonDocument docMin(4096), docMax(4096); - JsonVariant variantMin = docMin.to(); - JsonVariant variantMax = docMax.to(); - - T min = std::numeric_limits::min(); - T max = std::numeric_limits::max(); - - variantMin.set(min); - variantMax.set(max); - - REQUIRE(min == variantMin.as()); - REQUIRE(max == variantMax.as()); -} - -TEST_CASE("JsonVariant set()/get()") { -#if ARDUINOJSON_USE_LONG_LONG - SECTION("SizeOfJsonInteger") { - REQUIRE(8 == sizeof(JsonInteger)); - } -#endif - - SECTION("Null") { - checkValue(NULL); - } - SECTION("const char*") { - checkValue("hello"); - } - SECTION("std::string") { - checkValue("hello"); + REQUIRE(var.get("value") == 42); } - SECTION("False") { - checkValue(false); - } - SECTION("True") { - checkValue(true); + SECTION("get(std::string)") { + var["value"] = 42; + + REQUIRE(var.get(std::string("value")) == 42); } - SECTION("Double") { - checkNumericType(); - } - SECTION("Float") { - checkNumericType(); - } - SECTION("Char") { - checkNumericType(); - } - SECTION("SChar") { - checkNumericType(); - } - SECTION("SInt") { - checkNumericType(); - } - SECTION("SLong") { - checkNumericType(); - } - SECTION("SShort") { - checkNumericType(); - } - SECTION("UChar") { - checkNumericType(); - } - SECTION("UInt") { - checkNumericType(); - } - SECTION("ULong") { - checkNumericType(); - } - SECTION("UShort") { - checkNumericType(); - } -#if ARDUINOJSON_USE_LONG_LONG - SECTION("LongLong") { - checkNumericType(); - } - SECTION("ULongLong") { - checkNumericType(); - } -#endif + SECTION("get(int)") { + var.add().set(42); - SECTION("Int8") { - checkNumericType(); - } - SECTION("Uint8") { - checkNumericType(); - } - SECTION("Int16") { - checkNumericType(); - } - SECTION("Uint16") { - checkNumericType(); - } - SECTION("Int32") { - checkNumericType(); - } - SECTION("Uint32") { - checkNumericType(); - } -#if ARDUINOJSON_USE_LONG_LONG - SECTION("Int64") { - checkNumericType(); - } - SECTION("Uint64") { - checkNumericType(); - } -#endif - - SECTION("CanStoreObject") { - DynamicJsonDocument doc(4096); - JsonObject object = doc.to(); - - checkValue(object); + REQUIRE(var.get(0) == 42); } } diff --git a/test/JsonVariant/subscript.cpp b/test/JsonVariant/subscript.cpp index 2715c0aa..b56b5076 100644 --- a/test/JsonVariant/subscript.cpp +++ b/test/JsonVariant/subscript.cpp @@ -30,7 +30,8 @@ TEST_CASE("JsonVariant::operator[]") { array.add("element at index 1"); REQUIRE(2 == var.size()); - REQUIRE(std::string("element at index 0") == var[0]); + var[0].as(); + // REQUIRE(std::string("element at index 0") == ); REQUIRE(std::string("element at index 1") == var[1]); REQUIRE(std::string("element at index 0") == var[static_cast(0)]); // issue #381 @@ -171,4 +172,24 @@ TEST_CASE("JsonVariantConst::operator[]") { REQUIRE(cvar[0].isNull()); } } + + SECTION("Auto promote null JsonVariant to JsonObject") { + var["hello"] = "world"; + + REQUIRE(var.is() == true); + } + + SECTION("Don't auto promote non-null JsonVariant to JsonObject") { + var.set(42); + var["hello"] = "world"; + + REQUIRE(var.is() == false); + } + + SECTION("Don't auto promote null JsonVariant to JsonObject when reading") { + const char* value = var["hello"]; + + REQUIRE(var.is() == false); + REQUIRE(value == 0); + } } diff --git a/test/JsonVariant/types.cpp b/test/JsonVariant/types.cpp new file mode 100644 index 00000000..55214b10 --- /dev/null +++ b/test/JsonVariant/types.cpp @@ -0,0 +1,140 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include +#include +#include + +template +void checkValue(T expected) { + DynamicJsonDocument doc(4096); + JsonVariant variant = doc.to(); + + variant.set(expected); + REQUIRE(expected == variant.as()); +} + +template +void checkReference(T &expected) { + JsonVariant variant = expected; + REQUIRE(expected == variant.as()); +} + +template +void checkNumericType() { + DynamicJsonDocument docMin(4096), docMax(4096); + JsonVariant variantMin = docMin.to(); + JsonVariant variantMax = docMax.to(); + + T min = std::numeric_limits::min(); + T max = std::numeric_limits::max(); + + variantMin.set(min); + variantMax.set(max); + + REQUIRE(min == variantMin.as()); + REQUIRE(max == variantMax.as()); +} + +TEST_CASE("JsonVariant set()/get()") { +#if ARDUINOJSON_USE_LONG_LONG + SECTION("SizeOfJsonInteger") { + REQUIRE(8 == sizeof(JsonInteger)); + } +#endif + + SECTION("Null") { + checkValue(NULL); + } + SECTION("const char*") { + checkValue("hello"); + } + SECTION("std::string") { + checkValue("hello"); + } + + SECTION("False") { + checkValue(false); + } + SECTION("True") { + checkValue(true); + } + + SECTION("Double") { + checkNumericType(); + } + SECTION("Float") { + checkNumericType(); + } + SECTION("Char") { + checkNumericType(); + } + SECTION("SChar") { + checkNumericType(); + } + SECTION("SInt") { + checkNumericType(); + } + SECTION("SLong") { + checkNumericType(); + } + SECTION("SShort") { + checkNumericType(); + } + SECTION("UChar") { + checkNumericType(); + } + SECTION("UInt") { + checkNumericType(); + } + SECTION("ULong") { + checkNumericType(); + } + SECTION("UShort") { + checkNumericType(); + } +#if ARDUINOJSON_USE_LONG_LONG + SECTION("LongLong") { + checkNumericType(); + } + SECTION("ULongLong") { + checkNumericType(); + } +#endif + + SECTION("Int8") { + checkNumericType(); + } + SECTION("Uint8") { + checkNumericType(); + } + SECTION("Int16") { + checkNumericType(); + } + SECTION("Uint16") { + checkNumericType(); + } + SECTION("Int32") { + checkNumericType(); + } + SECTION("Uint32") { + checkNumericType(); + } +#if ARDUINOJSON_USE_LONG_LONG + SECTION("Int64") { + checkNumericType(); + } + SECTION("Uint64") { + checkNumericType(); + } +#endif + + SECTION("CanStoreObject") { + DynamicJsonDocument doc(4096); + JsonObject object = doc.to(); + + checkValue(object); + } +} diff --git a/test/MemberProxy/CMakeLists.txt b/test/MemberProxy/CMakeLists.txt new file mode 100644 index 00000000..918a426a --- /dev/null +++ b/test/MemberProxy/CMakeLists.txt @@ -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) diff --git a/test/MemberProxy/add.cpp b/test/MemberProxy/add.cpp new file mode 100644 index 00000000..e3e11a53 --- /dev/null +++ b/test/MemberProxy/add.cpp @@ -0,0 +1,25 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +using namespace ARDUINOJSON_NAMESPACE; + +TEST_CASE("MemberProxy::add()") { + DynamicJsonDocument doc(4096); + MemberProxy mp = doc["hello"]; + + SECTION("add(int)") { + mp.add(42); + + REQUIRE(doc.as() == "{\"hello\":[42]}"); + } + + SECTION("add(const char*)") { + mp.add("world"); + + REQUIRE(doc.as() == "{\"hello\":[\"world\"]}"); + } +} diff --git a/test/MemberProxy/set.cpp b/test/MemberProxy/set.cpp new file mode 100644 index 00000000..1ffdf9a1 --- /dev/null +++ b/test/MemberProxy/set.cpp @@ -0,0 +1,25 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +using namespace ARDUINOJSON_NAMESPACE; + +TEST_CASE("MemberProxy::set()") { + DynamicJsonDocument doc(4096); + MemberProxy mp = doc["hello"]; + + SECTION("set(int)") { + mp.set(42); + + REQUIRE(doc.as() == "{\"hello\":42}"); + } + + SECTION("set(const char*)") { + mp.set("world"); + + REQUIRE(doc.as() == "{\"hello\":\"world\"}"); + } +} diff --git a/test/MemberProxy/subscript.cpp b/test/MemberProxy/subscript.cpp new file mode 100644 index 00000000..fd996d8d --- /dev/null +++ b/test/MemberProxy/subscript.cpp @@ -0,0 +1,19 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +using namespace ARDUINOJSON_NAMESPACE; + +TEST_CASE("MemberProxy::operator[]") { + DynamicJsonDocument doc(4096); + MemberProxy mp = doc["hello"]; + + SECTION("set integer") { + mp["world"] = 42; + + REQUIRE(doc.as() == "{\"hello\":{\"world\":42}}"); + } +} diff --git a/test/Misc/TypeTraits.cpp b/test/Misc/TypeTraits.cpp index 99b2abf3..851cf11a 100644 --- a/test/Misc/TypeTraits.cpp +++ b/test/Misc/TypeTraits.cpp @@ -54,10 +54,10 @@ TEST_CASE("Polyfills/type_traits") { CHECK(IsVisitable::value == true); CHECK(IsVisitable::value == true); CHECK(IsVisitable::value == true); - CHECK(IsVisitable::value == true); + CHECK(IsVisitable >::value == true); CHECK(IsVisitable::value == true); CHECK(IsVisitable::value == true); - CHECK(IsVisitable >::value == true); + CHECK((IsVisitable >::value == true)); CHECK(IsVisitable::value == true); CHECK(IsVisitable::value == true); CHECK(IsVisitable >::value == true); diff --git a/test/Misc/unsigned_char.cpp b/test/Misc/unsigned_char.cpp index 4201e6b3..5160bc1e 100644 --- a/test/Misc/unsigned_char.cpp +++ b/test/Misc/unsigned_char.cpp @@ -147,7 +147,7 @@ TEST_CASE("unsigned char[]") { } } - SECTION("JsonObjectSubscript") { + SECTION("MemberProxy") { SECTION("operator=") { // issue #416 unsigned char value[] = "world"; @@ -181,7 +181,7 @@ TEST_CASE("unsigned char[]") { } } - SECTION("JsonArraySubscript") { + SECTION("ElementProxy") { SECTION("set()") { unsigned char value[] = "world"; diff --git a/test/MsgPackSerializer/misc.cpp b/test/MsgPackSerializer/misc.cpp index cd54a3da..52c53a7b 100644 --- a/test/MsgPackSerializer/misc.cpp +++ b/test/MsgPackSerializer/misc.cpp @@ -12,7 +12,7 @@ void check(T value, const std::string &expected) { REQUIRE(expected.size() == returnValue); } -TEST_CASE("serializeMsgPack(JsonObjectSubscript)") { +TEST_CASE("serializeMsgPack(MemberProxy)") { DynamicJsonDocument doc(4096); deserializeJson(doc, "{\"hello\":42}"); JsonObject obj = doc.as(); @@ -23,7 +23,7 @@ TEST_CASE("serializeMsgPack(JsonObjectSubscript)") { REQUIRE(result == "*"); } -TEST_CASE("serializeMsgPack(JsonArraySubscript)") { +TEST_CASE("serializeMsgPack(ElementProxy)") { DynamicJsonDocument doc(4096); deserializeJson(doc, "[42]"); JsonArray arr = doc.as();