diff --git a/CHANGELOG.md b/CHANGELOG.md index 01c7cc98..65738860 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ -Arduino JSON: change log -======================== +ArduinoJson: change log +======================= + +v5.0 (currently in development) +---- + +* Redesigned `JsonVariant` to leverage converting constructors instead of assignment operators. + +**BREAKING CHANGES**: +- `JsonObject::add()` was renamed to `set()` +- `JsonArray::at()` and `JsonObject::at()` were renamed to `get()` +- Number of digits of floating point value are now set with `double_with_n_digits()` v4.4 ---- diff --git a/README.md b/README.md index ae744885..336233ee 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,9 @@ Features * JSON decoding * JSON encoding (with optional indentation) * Elegant API, very easy to use -* Fixed memory allocation (no malloc) +* Efficient (no malloc, nor copy) +* Portable (written in C++98) +* Self-contained (no external dependency) * Small footprint * MIT License diff --git a/examples/JsonGeneratorExample/JsonGeneratorExample.ino b/examples/JsonGeneratorExample/JsonGeneratorExample.ino index d8656e6b..51711ef9 100644 --- a/examples/JsonGeneratorExample/JsonGeneratorExample.ino +++ b/examples/JsonGeneratorExample/JsonGeneratorExample.ino @@ -16,8 +16,8 @@ void setup() { root["time"] = 1351824120; JsonArray& data = root.createNestedArray("data"); - data.add(48.756080, 6); // 6 is the number of decimals to print - data.add(2.302038, 6); // if not specified, 2 digits are printed + data.add(double_with_n_digits(48.756080, 6)); + data.add(double_with_n_digits(2.302038, 6)); root.printTo(Serial); // This prints: diff --git a/include/ArduinoJson.h b/include/ArduinoJson.h index 95a5e347..2eacc7cd 100644 --- a/include/ArduinoJson.h +++ b/include/ArduinoJson.h @@ -4,9 +4,9 @@ // Arduino JSON library // https://github.com/bblanchon/ArduinoJson -#include "../include/ArduinoJson/DynamicJsonBuffer.hpp" -#include "../include/ArduinoJson/JsonArray.hpp" -#include "../include/ArduinoJson/JsonObject.hpp" -#include "../include/ArduinoJson/StaticJsonBuffer.hpp" +#include "ArduinoJson/DynamicJsonBuffer.hpp" +#include "ArduinoJson/JsonArray.hpp" +#include "ArduinoJson/JsonObject.hpp" +#include "ArduinoJson/StaticJsonBuffer.hpp" using namespace ArduinoJson; diff --git a/include/ArduinoJson/Internals/ForceInline.hpp b/include/ArduinoJson/Internals/ForceInline.hpp new file mode 100644 index 00000000..fd43e7a7 --- /dev/null +++ b/include/ArduinoJson/Internals/ForceInline.hpp @@ -0,0 +1,13 @@ +// Copyright Benoit Blanchon 2014-2015 +// MIT License +// +// Arduino JSON library +// https://github.com/bblanchon/ArduinoJson + +#pragma once + +#ifdef _MSC_VER +#define JSON_FORCE_INLINE __forceinline +#else +#define JSON_FORCE_INLINE __attribute__((always_inline)) +#endif diff --git a/include/ArduinoJson/Internals/JsonParser.hpp b/include/ArduinoJson/Internals/JsonParser.hpp index 1433de1a..c244ca8e 100644 --- a/include/ArduinoJson/Internals/JsonParser.hpp +++ b/include/ArduinoJson/Internals/JsonParser.hpp @@ -28,11 +28,17 @@ class JsonParser { bool skip(const char *wordToSkip); void skipSpaces(); - void parseAnythingTo(JsonVariant &destination); - inline void parseBooleanTo(JsonVariant &destination); - inline void parseNullTo(JsonVariant &destination); - inline void parseNumberTo(JsonVariant &destination); - inline const char *parseString(); + bool parseAnythingTo(JsonVariant *destination); + JSON_FORCE_INLINE bool parseAnythingToUnsafe(JsonVariant *destination); + + const char *parseString(); + + inline bool parseArrayTo(JsonVariant *destination); + inline bool parseBooleanTo(JsonVariant *destination); + inline bool parseNullTo(JsonVariant *destination); + inline bool parseNumberTo(JsonVariant *destination); + inline bool parseObjectTo(JsonVariant *destination); + inline bool parseStringTo(JsonVariant *destination); JsonBuffer *_buffer; char *_ptr; diff --git a/include/ArduinoJson/Internals/JsonVariantContent.hpp b/include/ArduinoJson/Internals/JsonVariantContent.hpp index 0f499ee0..a0330424 100644 --- a/include/ArduinoJson/Internals/JsonVariantContent.hpp +++ b/include/ArduinoJson/Internals/JsonVariantContent.hpp @@ -23,6 +23,11 @@ union JsonVariantContent { const char* asString; // asString can be null JsonArray* asArray; // asArray cannot be null JsonObject* asObject; // asObject cannot be null + + template + T as() const; }; } } + +#include "JsonVariantContent.ipp" diff --git a/include/ArduinoJson/Internals/JsonVariantContent.ipp b/include/ArduinoJson/Internals/JsonVariantContent.ipp new file mode 100644 index 00000000..2c5a9ae6 --- /dev/null +++ b/include/ArduinoJson/Internals/JsonVariantContent.ipp @@ -0,0 +1,96 @@ +// Copyright Benoit Blanchon 2014-2015 +// MIT License +// +// Arduino JSON library +// https://github.com/bblanchon/ArduinoJson + +#pragma once + +namespace ArduinoJson { + +// Forward declarations +class JsonArray; +class JsonObject; + +namespace Internals { +template <> +inline bool JsonVariantContent::as() const { + return asBoolean; +} + +template <> +inline char const* JsonVariantContent::as() const { + return asString; +} + +template <> +inline double JsonVariantContent::as() const { + return asDouble; +} + +template <> +inline float JsonVariantContent::as() const { + return static_cast(asDouble); +} + +template <> +inline JsonArray& JsonVariantContent::as() const { + return *asArray; +} + +template <> +inline const JsonArray& JsonVariantContent::as() const { + return *asArray; +} + +template <> +inline JsonObject& JsonVariantContent::as() const { + return *asObject; +} + +template <> +inline const JsonObject& JsonVariantContent::as() const { + return *asObject; +} + +template <> +inline signed char JsonVariantContent::as() const { + return static_cast(asLong); +} + +template <> +inline signed int JsonVariantContent::as() const { + return static_cast(asLong); +} + +template <> +inline signed long JsonVariantContent::as() const { + return static_cast(asLong); +} + +template <> +inline signed short JsonVariantContent::as() const { + return static_cast(asLong); +} + +template <> +inline unsigned char JsonVariantContent::as() const { + return static_cast(asLong); +} + +template <> +inline unsigned int JsonVariantContent::as() const { + return static_cast(asLong); +} + +template <> +inline unsigned long JsonVariantContent::as() const { + return static_cast(asLong); +} + +template <> +inline unsigned short JsonVariantContent::as() const { + return static_cast(asLong); +} +} +} diff --git a/include/ArduinoJson/Internals/JsonVariantType.hpp b/include/ArduinoJson/Internals/JsonVariantType.hpp index d9f177a6..0f49f958 100644 --- a/include/ArduinoJson/Internals/JsonVariantType.hpp +++ b/include/ArduinoJson/Internals/JsonVariantType.hpp @@ -7,12 +7,14 @@ #pragma once namespace ArduinoJson { +class JsonArray; +class JsonObject; + namespace Internals { // Enumerated type to know the current type of a JsonVariant. // The value determines which member of JsonVariantContent is used. enum JsonVariantType { - JSON_INVALID, // a special state for JsonVariant::invalid() JSON_UNDEFINED, // the JsonVariant has not been initialized JSON_ARRAY, // the JsonVariant stores a pointer to a JsonArray JSON_OBJECT, // the JsonVariant stores a pointer to a JsonObject diff --git a/include/ArduinoJson/Internals/List.hpp b/include/ArduinoJson/Internals/List.hpp index 760ca4f2..675ed3b2 100644 --- a/include/ArduinoJson/Internals/List.hpp +++ b/include/ArduinoJson/Internals/List.hpp @@ -39,7 +39,7 @@ class List { // Returns the numbers of elements in the list. // For a JsonObject, it would return the number of key-value pairs - int size() const; + size_t size() const; iterator begin() { return iterator(_firstNode); } iterator end() { return iterator(NULL); } @@ -48,19 +48,20 @@ class List { const_iterator end() const { return const_iterator(NULL); } protected: - node_type *createNode() { + node_type *addNewNode() { if (!_buffer) return NULL; - return new (_buffer) node_type(); - } - void addNode(node_type *nodeToAdd) { + node_type *newNode = new (_buffer) node_type(); + if (_firstNode) { node_type *lastNode = _firstNode; while (lastNode->next) lastNode = lastNode->next; - lastNode->next = nodeToAdd; + lastNode->next = newNode; } else { - _firstNode = nodeToAdd; + _firstNode = newNode; } + + return newNode; } void removeNode(node_type *nodeToRemove); diff --git a/include/ArduinoJson/Internals/Prettyfier.hpp b/include/ArduinoJson/Internals/Prettyfier.hpp index edd90920..bbecc4dc 100644 --- a/include/ArduinoJson/Internals/Prettyfier.hpp +++ b/include/ArduinoJson/Internals/Prettyfier.hpp @@ -31,7 +31,7 @@ class Prettyfier : public Print { size_t handleBlockClose(uint8_t); size_t handleBlockOpen(uint8_t); - size_t handleColumn(); + size_t handleColon(); size_t handleComma(); size_t handleQuoteOpen(); size_t handleNormalChar(uint8_t); diff --git a/include/ArduinoJson/JsonArray.hpp b/include/ArduinoJson/JsonArray.hpp index 3a54518d..78e58069 100644 --- a/include/ArduinoJson/JsonArray.hpp +++ b/include/ArduinoJson/JsonArray.hpp @@ -22,6 +22,7 @@ namespace ArduinoJson { // Forward declarations class JsonObject; class JsonBuffer; +class JsonArraySubscript; // An array of JsonVariant. // @@ -33,35 +34,35 @@ class JsonArray : public Internals::JsonPrintable, public Internals::ReferenceType, public Internals::List, public Internals::JsonBufferAllocated { - // JsonBuffer is a friend because it needs to call the private constructor. - friend class JsonBuffer; - public: - // Returns the JsonVariant at the specified index (synonym for operator[]) - JsonVariant &at(int index) const; + // Create an empty JsonArray attached to the specified JsonBuffer. + // You should not call this constructor directly. + // Instead, use JsonBuffer::createArray() or JsonBuffer::parseArray(). + explicit JsonArray(JsonBuffer *buffer) + : Internals::List(buffer) {} - // Returns the JsonVariant at the specified index (synonym for at()) - JsonVariant &operator[](int index) const { return at(index); } + // Gets the value at the specified index + JSON_FORCE_INLINE const JsonArraySubscript operator[](size_t index) const; - // Adds an uninitialized JsonVariant at the end of the array. - // Return a reference or JsonVariant::invalid() if allocation fails. - JsonVariant &add(); + // Gets or sets the value at specified index + JSON_FORCE_INLINE JsonArraySubscript operator[](size_t index); // Adds the specified value at the end of the array. + JSON_FORCE_INLINE bool add(const JsonVariant value); + + // Sets the value at specified index. + JSON_FORCE_INLINE void set(size_t index, const JsonVariant value); + + // Gets the value at the specified index. + JSON_FORCE_INLINE JsonVariant get(size_t index) const; + + // Gets the value at the specified index. template - void add(T value) { - add().set(value); - } + JSON_FORCE_INLINE T get(size_t index) const; - // Adds the specified double value at the end of the array. - // The value will be printed with the specified number of decimal digits. - void add(double value, uint8_t decimals) { add().set(value, decimals); } - - // Adds a reference to the specified JsonArray at the end of the array. - void add(JsonArray &array) { add().set(array); } - - // Adds a reference to the specified JsonObject at the end of the array. - void add(JsonObject &obejct) { add().set(obejct); } + // Check the type of the value at specified index. + template + JSON_FORCE_INLINE T is(size_t index) const; // Creates a JsonArray and adds a reference at the end of the array. // It's a shortcut for JsonBuffer::createArray() and JsonArray::add() @@ -72,7 +73,7 @@ class JsonArray : public Internals::JsonPrintable, JsonObject &createNestedObject(); // Removes element at specified index. - void removeAt(int index); + void removeAt(size_t index); // Returns a reference an invalid JsonArray. // This object is meant to replace a NULL pointer. @@ -83,13 +84,11 @@ class JsonArray : public Internals::JsonPrintable, void writeTo(Internals::JsonWriter &writer) const; private: - // Create an empty JsonArray attached to the specified JsonBuffer. - explicit JsonArray(JsonBuffer *buffer) - : Internals::List(buffer) {} - - node_type *getNodeAt(int index) const; + node_type *getNodeAt(size_t index) const; // The instance returned by JsonArray::invalid() static JsonArray _invalid; }; } + +#include "JsonArray.ipp" diff --git a/include/ArduinoJson/JsonArray.ipp b/include/ArduinoJson/JsonArray.ipp new file mode 100644 index 00000000..e21da630 --- /dev/null +++ b/include/ArduinoJson/JsonArray.ipp @@ -0,0 +1,65 @@ +// Copyright Benoit Blanchon 2014-2015 +// MIT License +// +// Arduino JSON library +// https://github.com/bblanchon/ArduinoJson + +#pragma once + +#include "JsonArray.hpp" +#include "JsonArraySubscript.hpp" + +namespace ArduinoJson { + +inline JsonArraySubscript JsonArray::operator[](size_t index) { + return JsonArraySubscript(*this, index); +} + +inline const JsonArraySubscript JsonArray::operator[](size_t index) const { + return JsonArraySubscript(*const_cast(this), index); +} + +inline bool JsonArray::add(const JsonVariant value) { + node_type *node = addNewNode(); + if (node) node->content = value; + return node != NULL; +} + +inline JsonVariant JsonArray::get(size_t index) const { + node_type *node = getNodeAt(index); + return node ? node->content : JsonVariant(); +} + +template +inline T JsonArray::get(size_t index) const { + node_type *node = getNodeAt(index); + return node ? node->content.as() : JsonVariant::invalid(); +} + +template +inline T JsonArray::is(size_t index) const { + node_type *node = getNodeAt(index); + return node ? node->content.is() : false; +} + +inline void JsonArray::set(size_t index, const JsonVariant value) { + node_type *node = getNodeAt(index); + if (node) node->content = value; +} + +template +inline const JsonArraySubscript JsonVariantBase::operator[]( + int index) const { + return asArray()[index]; +} + +template <> +inline JsonArray &JsonVariant::invalid() { + return JsonArray::invalid(); +} + +template <> +inline JsonArray const &JsonVariant::invalid() { + return JsonArray::invalid(); +} +} diff --git a/include/ArduinoJson/JsonArraySubscript.hpp b/include/ArduinoJson/JsonArraySubscript.hpp new file mode 100644 index 00000000..52ba828f --- /dev/null +++ b/include/ArduinoJson/JsonArraySubscript.hpp @@ -0,0 +1,40 @@ +// Copyright Benoit Blanchon 2014-2015 +// MIT License +// +// Arduino JSON library +// https://github.com/bblanchon/ArduinoJson + +#pragma once + +#include "JsonVariantBase.hpp" + +namespace ArduinoJson { +class JsonArraySubscript : public JsonVariantBase { + public: + JSON_FORCE_INLINE JsonArraySubscript(JsonArray& array, size_t index) + : _array(array), _index(index) {} + + JSON_FORCE_INLINE JsonArraySubscript& operator=(const JsonVariant& value) { + _array.set(_index, value); + return *this; + } + + JSON_FORCE_INLINE bool success() const { return _index < _array.size(); } + + JSON_FORCE_INLINE operator JsonVariant() const { return _array.get(_index); } + + template + JSON_FORCE_INLINE T as() const { + return _array.get(_index); + } + + template + JSON_FORCE_INLINE T is() const { + return _array.is(_index); + } + + private: + JsonArray& _array; + const size_t _index; +}; +} diff --git a/include/ArduinoJson/JsonObject.hpp b/include/ArduinoJson/JsonObject.hpp index 41081d03..48d1ae18 100644 --- a/include/ArduinoJson/JsonObject.hpp +++ b/include/ArduinoJson/JsonObject.hpp @@ -22,6 +22,7 @@ namespace ArduinoJson { // Forward declarations class JsonArray; class JsonBuffer; +class JsonObjectSubscript; // A dictionary of JsonVariant indexed by string (char*) // @@ -33,44 +34,35 @@ class JsonObject : public Internals::JsonPrintable, public Internals::ReferenceType, public Internals::List, public Internals::JsonBufferAllocated { - // JsonBuffer is a friend because it needs to call the private constructor. - friend class JsonBuffer; - public: typedef const char *key_type; typedef JsonPair value_type; - // Gets the JsonVariant associated with the specified key. - // Returns a reference or JsonVariant::invalid() if not found. - JsonVariant &at(key_type key); + // Create an empty JsonArray attached to the specified JsonBuffer. + // You should not use this constructor directly. + // Instead, use JsonBuffer::createObject() or JsonBuffer.parseObject(). + JSON_FORCE_INLINE explicit JsonObject(JsonBuffer *buffer) + : Internals::List(buffer) {} - // Gets the JsonVariant associated with the specified key. - // Returns a constant reference or JsonVariant::invalid() if not found. - const JsonVariant &at(key_type key) const; + // Gets or sets the value associated with the specified key. + JSON_FORCE_INLINE JsonObjectSubscript operator[](key_type key); - // Gets or create the JsonVariant associated with the specified key. - // Returns a reference or JsonVariant::invalid() if allocation failed. - JsonVariant &operator[](key_type key); + // Gets the value associated with the specified key. + JSON_FORCE_INLINE const JsonObjectSubscript operator[](key_type key) const; - // Gets the JsonVariant associated with the specified key. - // Returns a constant reference or JsonVariant::invalid() if not found. - const JsonVariant &operator[](key_type key) const { return at(key); } + // Sets the specified key with the specified value. + JSON_FORCE_INLINE bool set(key_type key, const JsonVariant value); - // Adds an uninitialized JsonVariant associated with the specified key. - // Return a reference or JsonVariant::invalid() if allocation fails. - JsonVariant &add(key_type key) { return (*this)[key]; } + // Gets the value associated with the specified key. + JSON_FORCE_INLINE JsonVariant get(key_type key) const; - // Adds the specified key with the specified value. + // Gets the value associated with the specified key. template - void add(key_type key, T value) { - add(key).set(value); - } + JSON_FORCE_INLINE T get(key_type key) const; - // Adds the specified key with a reference to the specified JsonArray. - void add(key_type key, JsonArray &array) { add(key).set(array); } - - // Adds the specified key with a reference to the specified JsonObject. - void add(key_type key, JsonObject &object) { add(key).set(object); } + // Checks the type of the value associated with the specified key. + template + JSON_FORCE_INLINE T is(key_type key) const; // Creates and adds a JsonArray. // This is a shortcut for JsonBuffer::createArray() and JsonObject::add(). @@ -81,7 +73,7 @@ class JsonObject : public Internals::JsonPrintable, JsonObject &createNestedObject(key_type key); // Tells weither the specified key is present and associated with a value. - bool containsKey(key_type key) const { return at(key).success(); } + JSON_FORCE_INLINE bool containsKey(key_type key) const; // Removes the specified key and the associated value. void remove(key_type key); @@ -95,13 +87,14 @@ class JsonObject : public Internals::JsonPrintable, void writeTo(Internals::JsonWriter &writer) const; private: - // Create an empty JsonArray attached to the specified JsonBuffer. - explicit JsonObject(JsonBuffer *buffer) : Internals::List(buffer) {} - // Returns the list node that matches the specified key. node_type *getNodeAt(key_type key) const; + node_type *getOrCreateNodeAt(const char *key); + // The instance returned by JsonObject::invalid() static JsonObject _invalid; }; } + +#include "JsonObject.ipp" diff --git a/include/ArduinoJson/JsonObject.ipp b/include/ArduinoJson/JsonObject.ipp new file mode 100644 index 00000000..a0e3a43b --- /dev/null +++ b/include/ArduinoJson/JsonObject.ipp @@ -0,0 +1,69 @@ +// Copyright Benoit Blanchon 2014-2015 +// MIT License +// +// Arduino JSON library +// https://github.com/bblanchon/ArduinoJson + +#pragma once + +#include "JsonObject.hpp" +#include "JsonObjectSubscript.hpp" + +namespace ArduinoJson { + +inline JsonVariant JsonObject::get(key_type key) const { + node_type *node = getNodeAt(key); + return node ? node->content.value : JsonVariant(); +} + +template +inline T JsonObject::get(key_type key) const { + node_type *node = getNodeAt(key); + return node ? node->content.value.as() : JsonVariant::invalid(); +} + +template +inline T JsonObject::is(key_type key) const { + node_type *node = getNodeAt(key); + return node ? node->content.value.is() : false; +} + +inline JsonObjectSubscript JsonObject::operator[](key_type key) { + return JsonObjectSubscript(*this, key); +} + +inline const JsonObjectSubscript JsonObject::operator[](key_type key) const { + return JsonObjectSubscript(*const_cast(this), key); +} + +inline bool JsonObject::containsKey(key_type key) const { + return getNodeAt(key) != NULL; +} + +inline void JsonObject::remove(key_type key) { removeNode(getNodeAt(key)); } + +inline bool JsonObject::set(const char *key, const JsonVariant value) { + node_type *node = getOrCreateNodeAt(key); + if (!node) return false; + + node->content.key = key; + node->content.value = value; + return true; +} + +template +inline const JsonObjectSubscript JsonVariantBase::operator[]( + const char *key) const { + return asObject()[key]; +} + +template <> +inline JsonObject const &JsonVariant::invalid() { + return JsonObject::invalid(); +} + +template <> +inline JsonObject &JsonVariant::invalid() { + return JsonObject::invalid(); +} +} diff --git a/include/ArduinoJson/JsonObjectSubscript.hpp b/include/ArduinoJson/JsonObjectSubscript.hpp new file mode 100644 index 00000000..70f4a229 --- /dev/null +++ b/include/ArduinoJson/JsonObjectSubscript.hpp @@ -0,0 +1,40 @@ +// Copyright Benoit Blanchon 2014-2015 +// MIT License +// +// Arduino JSON library +// https://github.com/bblanchon/ArduinoJson + +#pragma once + +#include "JsonVariantBase.hpp" + +namespace ArduinoJson { +class JsonObjectSubscript : public JsonVariantBase { + public: + JSON_FORCE_INLINE JsonObjectSubscript(JsonObject& object, const char* key) + : _object(object), _key(key) {} + + JSON_FORCE_INLINE JsonObjectSubscript& operator=(const JsonVariant& value) { + _object.set(_key, value); + return *this; + } + + JSON_FORCE_INLINE bool success() const { return _object.containsKey(_key); } + + JSON_FORCE_INLINE operator JsonVariant() const { return _object.get(_key); } + + template + JSON_FORCE_INLINE T as() const { + return _object.get(_key); + } + + template + JSON_FORCE_INLINE T is() const { + return _object.is(_key); + } + + private: + JsonObject& _object; + const char* _key; +}; +} diff --git a/include/ArduinoJson/JsonVariant.hpp b/include/ArduinoJson/JsonVariant.hpp index 6e0de5fc..a093264f 100644 --- a/include/ArduinoJson/JsonVariant.hpp +++ b/include/ArduinoJson/JsonVariant.hpp @@ -12,6 +12,7 @@ #include "Internals/JsonPrintable.hpp" #include "Internals/JsonVariantContent.hpp" #include "Internals/JsonVariantType.hpp" +#include "JsonVariantBase.hpp" namespace ArduinoJson { @@ -26,261 +27,74 @@ class JsonObject; // - a char, short, int or a long (signed or unsigned) // - a string (const char*) // - a reference to a JsonArray or JsonObject -class JsonVariant : public Internals::JsonPrintable { +class JsonVariant : public Internals::JsonPrintable, + public JsonVariantBase { public: // Creates an uninitialized JsonVariant - JsonVariant() : _type(Internals::JSON_UNDEFINED) {} + JSON_FORCE_INLINE JsonVariant() : _type(Internals::JSON_UNDEFINED) {} - // Initializes a JsonVariant with the specified value. - template - explicit JsonVariant(T value) { - set(value); - } - - // Tells weither the variant is valid. - bool success() const { - return _type != Internals::JSON_INVALID && - _type != Internals::JSON_UNDEFINED; - } - - // Sets the variant to a boolean value. + // Create a JsonVariant containing a boolean value. // It will be serialized as "true" or "false" in JSON. - void set(bool value); + JSON_FORCE_INLINE JsonVariant(bool value); - // Sets the variant to a floating point value. + // Create a JsonVariant containing a floating point value. // The second argument specifies the number of decimal digits to write in // the JSON string. - void set(double value, uint8_t decimals = 2); + JSON_FORCE_INLINE JsonVariant(float value, uint8_t decimals = 2); + JSON_FORCE_INLINE JsonVariant(double value, uint8_t decimals = 2); - // Sets the variant to be an integer value. - void set(signed long value); - void set(signed char value) { set(static_cast(value)); } - void set(signed int value) { set(static_cast(value)); } - void set(signed short value) { set(static_cast(value)); } - void set(unsigned char value) { set(static_cast(value)); } - void set(unsigned int value) { set(static_cast(value)); } - void set(unsigned long value) { set(static_cast(value)); } - void set(unsigned short value) { set(static_cast(value)); } + // Create a JsonVariant containing an integer value. + JSON_FORCE_INLINE JsonVariant(signed char value); + JSON_FORCE_INLINE JsonVariant(signed long value); + JSON_FORCE_INLINE JsonVariant(signed int value); + JSON_FORCE_INLINE JsonVariant(signed short value); + JSON_FORCE_INLINE JsonVariant(unsigned char value); + JSON_FORCE_INLINE JsonVariant(unsigned long value); + JSON_FORCE_INLINE JsonVariant(unsigned int value); + JSON_FORCE_INLINE JsonVariant(unsigned short value); - // Sets the variant to be a string. - void set(const char *value); + // Create a JsonVariant containing a string. + JSON_FORCE_INLINE JsonVariant(const char *value); - // Sets the variant to be a reference to an array. - void set(JsonArray &array); + // Create a JsonVariant containing a reference to an array. + JSON_FORCE_INLINE JsonVariant(JsonArray &array); - // Sets the variant to be a reference to an object. - void set(JsonObject &object); - - // Sets the variant to the specified value. - template - JsonVariant &operator=(T value) { - set(value); - return *this; - } - - // Sets the variant to be a reference to an array. - JsonVariant &operator=(JsonArray &array) { - set(array); - return *this; - } - - // Sets the variant to be a reference to an object. - JsonVariant &operator=(JsonObject &object) { - set(object); - return *this; - } - - // Gets the variant as a boolean value. - // Returns false if the variant is not a boolean value. - operator bool() const; - - // Gets the variant as a floating-point value. - // Returns 0.0 if the variant is not a floating-point value - operator double() const; - operator float() const { return static_cast(as()); } - - // Gets the variant as an integer value. - // Returns 0 if the variant is not an integer value. - operator signed long() const; - operator signed char() const { return cast_long_to(); } - operator signed int() const { return cast_long_to(); } - operator signed short() const { return cast_long_to(); } - operator unsigned char() const { return cast_long_to(); } - operator unsigned int() const { return cast_long_to(); } - operator unsigned long() const { return cast_long_to(); } - operator unsigned short() const { return cast_long_to(); } - - // Gets the variant as a string. - // Returns NULL if variant is not a string. - operator const char *() const; - const char *asString() const { return as(); } - - // Gets the variant as an array. - // Returns a reference to the JsonArray or JsonArray::invalid() if the variant - // is not an array. - operator JsonArray &() const; - JsonArray &asArray() const { return as(); } - - // Gets the variant as an object. - // Returns a reference to the JsonObject or JsonObject::invalid() if the - // variant is not an object. - operator JsonObject &() const; - JsonObject &asObject() const { return as(); } + // Create a JsonVariant containing a reference to an object. + JSON_FORCE_INLINE JsonVariant(JsonObject &object); // Get the variant as the specified type. // See cast operators for details. template - T as() const { - return static_cast(*this); - } + JSON_FORCE_INLINE T as() const; // Tells weither the variant has the specified type. // Returns true if the variant has type type T, false otherwise. template - bool is() const { - return false; - } - - // Returns an invalid variant. - // This is meant to replace a NULL pointer. - static JsonVariant &invalid() { return _invalid; } + JSON_FORCE_INLINE bool is() const; // Serialize the variant to a JsonWriter void writeTo(Internals::JsonWriter &writer) const; - // Mimics an array or an object. - // Returns the size of the array or object if the variant has that type. - // Returns 0 if the variant is neither an array nor an object - size_t size() const; - - // Mimics an array. - // Returns the element at specified index if the variant is an array. - // Returns JsonVariant::invalid() if the variant is not an array. - JsonVariant &operator[](int index); - - // Mimics an object. - // Returns the value associated with the specified key if the variant is an - // object. - // Return JsonVariant::invalid() if the variant is not an object. - JsonVariant &operator[](const char *key); + // TODO: rename + template + static T invalid(); private: - // Special constructor used only to create _invalid. - explicit JsonVariant(Internals::JsonVariantType type) : _type(type) {} - - // Helper for interger cast operators - template - T cast_long_to() const { - return static_cast(as()); - } - // The current type of the variant Internals::JsonVariantType _type; // The various alternatives for the value of the variant. Internals::JsonVariantContent _content; - - // The instance returned by JsonVariant::invalid() - static JsonVariant _invalid; }; -template <> -inline bool JsonVariant::is() const { - return _type == Internals::JSON_LONG; +inline JsonVariant float_with_n_digits(float value, uint8_t digits) { + return JsonVariant(value, digits); } -template <> -inline bool JsonVariant::is() const { - return _type >= Internals::JSON_DOUBLE_0_DECIMALS; +inline JsonVariant double_with_n_digits(double value, uint8_t digits) { + return JsonVariant(value, digits); +} } -template <> -inline bool JsonVariant::is() const { - return _type == Internals::JSON_BOOLEAN; -} - -template <> -inline bool JsonVariant::is() const { - return _type == Internals::JSON_STRING; -} - -template <> -inline bool JsonVariant::is() const { - return _type == Internals::JSON_ARRAY; -} - -template <> -inline bool JsonVariant::is() const { - return _type == Internals::JSON_ARRAY; -} - -template <> -inline bool JsonVariant::is() const { - return _type == Internals::JSON_OBJECT; -} - -template <> -inline bool JsonVariant::is() const { - return _type == Internals::JSON_OBJECT; -} - -template -inline bool operator==(const JsonVariant &left, T right) { - return left.as() == right; -} - -template -inline bool operator==(T left, const JsonVariant &right) { - return left == right.as(); -} - -template -inline bool operator!=(const JsonVariant &left, T right) { - return left.as() != right; -} - -template -inline bool operator!=(T left, const JsonVariant &right) { - return left != right.as(); -} - -template -inline bool operator<=(const JsonVariant &left, T right) { - return left.as() <= right; -} - -template -inline bool operator<=(T left, const JsonVariant &right) { - return left <= right.as(); -} - -template -inline bool operator>=(const JsonVariant &left, T right) { - return left.as() >= right; -} - -template -inline bool operator>=(T left, const JsonVariant &right) { - return left >= right.as(); -} - -template -inline bool operator<(const JsonVariant &left, T right) { - return left.as() < right; -} - -template -inline bool operator<(T left, const JsonVariant &right) { - return left < right.as(); -} - -template -inline bool operator>(const JsonVariant &left, T right) { - return left.as() > right; -} - -template -inline bool operator>(T left, const JsonVariant &right) { - return left > right.as(); -} -} +// Include inline implementations +#include "JsonVariant.ipp" diff --git a/include/ArduinoJson/JsonVariant.ipp b/include/ArduinoJson/JsonVariant.ipp new file mode 100644 index 00000000..c4136627 --- /dev/null +++ b/include/ArduinoJson/JsonVariant.ipp @@ -0,0 +1,179 @@ +// Copyright Benoit Blanchon 2014-2015 +// MIT License +// +// Arduino JSON library +// https://github.com/bblanchon/ArduinoJson + +#pragma once + +#include "JsonVariant.hpp" + +namespace ArduinoJson { + +inline JsonVariant::JsonVariant(bool value) { + _type = Internals::JSON_BOOLEAN; + _content.asBoolean = value; +} + +inline JsonVariant::JsonVariant(const char *value) { + _type = Internals::JSON_STRING; + _content.asString = value; +} + +inline JsonVariant::JsonVariant(double value, uint8_t decimals) { + _type = static_cast( + Internals::JSON_DOUBLE_0_DECIMALS + decimals); + _content.asDouble = value; +} + +inline JsonVariant::JsonVariant(float value, uint8_t decimals) { + _type = static_cast( + Internals::JSON_DOUBLE_0_DECIMALS + decimals); + _content.asDouble = value; +} + +inline JsonVariant::JsonVariant(JsonArray &array) { + _type = Internals::JSON_ARRAY; + _content.asArray = &array; +} + +inline JsonVariant::JsonVariant(JsonObject &object) { + _type = Internals::JSON_OBJECT; + _content.asObject = &object; +} + +inline JsonVariant::JsonVariant(signed char value) { + _type = Internals::JSON_LONG; + _content.asLong = value; +} + +inline JsonVariant::JsonVariant(signed int value) { + _type = Internals::JSON_LONG; + _content.asLong = value; +} + +inline JsonVariant::JsonVariant(signed long value) { + _type = Internals::JSON_LONG; + _content.asLong = value; +} + +inline JsonVariant::JsonVariant(signed short value) { + _type = Internals::JSON_LONG; + _content.asLong = value; +} + +inline JsonVariant::JsonVariant(unsigned char value) { + _type = Internals::JSON_LONG; + _content.asLong = value; +} + +inline JsonVariant::JsonVariant(unsigned int value) { + _type = Internals::JSON_LONG; + _content.asLong = value; +} + +inline JsonVariant::JsonVariant(unsigned long value) { + _type = Internals::JSON_LONG; + _content.asLong = value; +} + +inline JsonVariant::JsonVariant(unsigned short value) { + _type = Internals::JSON_LONG; + _content.asLong = value; +} + +template +inline T JsonVariant::as() const { + return is() ? _content.as() : invalid(); +} + +template +inline T JsonVariant::invalid() { + return T(); +} + +template +inline bool JsonVariant::is() const { + return false; +} + +template <> +inline bool JsonVariant::is() const { + return _type == Internals::JSON_BOOLEAN; +} + +template <> +inline bool JsonVariant::is() const { + return _type == Internals::JSON_STRING; +} + +template <> +inline bool JsonVariant::is() const { + return _type >= Internals::JSON_DOUBLE_0_DECIMALS; +} + +template <> +inline bool JsonVariant::is() const { + return _type >= Internals::JSON_DOUBLE_0_DECIMALS; +} + +template <> +inline bool JsonVariant::is() const { + return _type == Internals::JSON_ARRAY; +} + +template <> +inline bool JsonVariant::is() const { + return _type == Internals::JSON_ARRAY; +} + +template <> +inline bool JsonVariant::is() const { + return _type == Internals::JSON_OBJECT; +} + +template <> +inline bool JsonVariant::is() const { + return _type == Internals::JSON_OBJECT; +} + +template <> +inline bool JsonVariant::is() const { + return _type == Internals::JSON_LONG; +} + +template <> +inline bool JsonVariant::is() const { + return _type == Internals::JSON_LONG; +} + +template <> +inline bool JsonVariant::is() const { + return _type == Internals::JSON_LONG; +} + +template <> +inline bool JsonVariant::is() const { + return _type == Internals::JSON_LONG; +} + +template <> +inline bool JsonVariant::is() const { + return _type == Internals::JSON_LONG; +} + +template <> +inline bool JsonVariant::is() const { + return _type == Internals::JSON_LONG; +} + +template <> +inline bool JsonVariant::is() const { + return _type == Internals::JSON_LONG; +} + +template <> +inline bool JsonVariant::is() const { + return _type == Internals::JSON_LONG; +} +} diff --git a/include/ArduinoJson/JsonVariantBase.hpp b/include/ArduinoJson/JsonVariantBase.hpp new file mode 100644 index 00000000..c9a331ad --- /dev/null +++ b/include/ArduinoJson/JsonVariantBase.hpp @@ -0,0 +1,148 @@ +// Copyright Benoit Blanchon 2014-2015 +// MIT License +// +// Arduino JSON library +// https://github.com/bblanchon/ArduinoJson + +#pragma once + +#include "Internals/ForceInline.hpp" + +namespace ArduinoJson { + +// Forward declarations. +class JsonArraySubscript; +class JsonObjectSubscript; + +template +class JsonVariantBase { + public: + // Gets the variant as a boolean value. + // Returns false if the variant is not a boolean value. + JSON_FORCE_INLINE operator bool() const { return as(); } + + // Gets the variant as a floating-point value. + // Returns 0.0 if the variant is not a floating-point value + JSON_FORCE_INLINE operator double() const { return as(); } + JSON_FORCE_INLINE operator float() const { return as(); } + + // Gets the variant as an integer value. + // Returns 0 if the variant is not an integer value. + JSON_FORCE_INLINE operator signed long() const { return as(); } + JSON_FORCE_INLINE operator signed char() const { return as(); } + JSON_FORCE_INLINE operator signed int() const { return as(); } + JSON_FORCE_INLINE operator signed short() const { return as(); } + JSON_FORCE_INLINE operator unsigned char() const { + return as(); + } + JSON_FORCE_INLINE operator unsigned int() const { return as(); } + JSON_FORCE_INLINE operator unsigned long() const { + return as(); + } + JSON_FORCE_INLINE operator unsigned short() const { + return as(); + } + + // Gets the variant as a string. + // Returns NULL if variant is not a string. + JSON_FORCE_INLINE operator const char *() const { return as(); } + JSON_FORCE_INLINE const char *asString() const { return as(); } + + // Gets the variant as an array. + // Returns a reference to the JsonArray or JsonArray::invalid() if the + // variant + // is not an array. + JSON_FORCE_INLINE operator JsonArray &() const { return as(); } + JSON_FORCE_INLINE JsonArray &asArray() const { return as(); } + + // Gets the variant as an object. + // Returns a reference to the JsonObject or JsonObject::invalid() if the + // variant is not an object. + JSON_FORCE_INLINE operator JsonObject &() const { return as(); } + JSON_FORCE_INLINE JsonObject &asObject() const { return as(); } + + template + JSON_FORCE_INLINE const T as() const { + return impl()->template as(); + } + + // Mimics an array or an object. + // Returns the size of the array or object if the variant has that type. + // Returns 0 if the variant is neither an array nor an object + size_t size() const { return asArray().size() + asObject().size(); } + + // Mimics an array. + // Returns the element at specified index if the variant is an array. + // Returns JsonVariant::invalid() if the variant is not an array. + JSON_FORCE_INLINE const JsonArraySubscript operator[](int index) const; + + // Mimics an object. + // Returns the value associated with the specified key if the variant is + // an object. + // Return JsonVariant::invalid() if the variant is not an object. + JSON_FORCE_INLINE const JsonObjectSubscript operator[](const char *key) const; + + private: + const TImpl *impl() const { return static_cast(this); } +}; + +template +inline bool operator==(const JsonVariantBase &left, TComparand right) { + return left.template as() == right; +} + +template +inline bool operator==(TComparand left, const JsonVariantBase &right) { + return left == right.template as(); +} + +template +inline bool operator!=(const JsonVariantBase &left, TComparand right) { + return left.template as() != right; +} + +template +inline bool operator!=(TComparand left, const JsonVariantBase &right) { + return left != right.template as(); +} + +template +inline bool operator<=(const JsonVariantBase &left, TComparand right) { + return left.template as() <= right; +} + +template +inline bool operator<=(TComparand left, const JsonVariantBase &right) { + return left <= right.template as(); +} + +template +inline bool operator>=(const JsonVariantBase &left, TComparand right) { + return left.template as() >= right; +} + +template +inline bool operator>=(TComparand left, const JsonVariantBase &right) { + return left >= right.template as(); +} + +template +inline bool operator<(const JsonVariantBase &left, TComparand right) { + return left.template as() < right; +} + +template +inline bool operator<(TComparand left, const JsonVariantBase &right) { + return left < right.template as(); +} + +template +inline bool operator>(const JsonVariantBase &left, TComparand right) { + return left.template as() > right; +} + +template +inline bool operator>(TComparand left, const JsonVariantBase &right) { + return left > right.template as(); +} +} diff --git a/src/Internals/JsonParser.cpp b/src/Internals/JsonParser.cpp index ff60cff0..2f7e7edd 100644 --- a/src/Internals/JsonParser.cpp +++ b/src/Internals/JsonParser.cpp @@ -38,25 +38,27 @@ bool JsonParser::skip(const char *wordToSkip) { return *charToSkip == '\0'; } -void JsonParser::parseAnythingTo(JsonVariant &destination) { - if (_nestingLimit == 0) return; +bool JsonParser::parseAnythingTo(JsonVariant *destination) { + if (_nestingLimit == 0) return false; _nestingLimit--; + bool success = parseAnythingToUnsafe(destination); + _nestingLimit++; + return success; +} +inline bool JsonParser::parseAnythingToUnsafe(JsonVariant *destination) { skipSpaces(); switch (*_ptr) { case '[': - destination = parseArray(); - break; + return parseArrayTo(destination); case '{': - destination = parseObject(); - break; + return parseObjectTo(destination); case 't': case 'f': - parseBooleanTo(destination); - break; + return parseBooleanTo(destination); case '-': case '.': @@ -70,20 +72,14 @@ void JsonParser::parseAnythingTo(JsonVariant &destination) { case '7': case '8': case '9': - parseNumberTo(destination); - break; + return parseNumberTo(destination); case 'n': - parseNullTo(destination); - break; + return parseNullTo(destination); - case '\'': - case '\"': - destination = parseString(); - break; + default: + return parseStringTo(destination); } - - _nestingLimit++; } JsonArray &JsonParser::parseArray() { @@ -97,9 +93,9 @@ JsonArray &JsonParser::parseArray() { // Read each value for (;;) { // 1 - Parse value - JsonVariant &value = array.add(); - parseAnythingTo(value); - if (!value.success()) goto ERROR_INVALID_VALUE; + JsonVariant value; + if (!parseAnythingTo(&value)) goto ERROR_INVALID_VALUE; + if (!array.add(value)) goto ERROR_NO_MEMORY; // 2 - More values? if (skip(']')) goto SUCCES_NON_EMPTY_ARRAY; @@ -113,9 +109,18 @@ SUCCES_NON_EMPTY_ARRAY: ERROR_INVALID_VALUE: ERROR_MISSING_BRACKET: ERROR_MISSING_COMMA: +ERROR_NO_MEMORY: return JsonArray::invalid(); } +bool JsonParser::parseArrayTo(JsonVariant *destination) { + JsonArray &array = parseArray(); + if (!array.success()) return false; + + *destination = array; + return true; +} + JsonObject &JsonParser::parseObject() { // Create an empty object JsonObject &object = _buffer->createObject(); @@ -132,9 +137,9 @@ JsonObject &JsonParser::parseObject() { if (!skip(':')) goto ERROR_MISSING_COLON; // 2 - Parse value - JsonVariant &value = object.add(key); - parseAnythingTo(value); - if (!value.success()) goto ERROR_INVALID_VALUE; + JsonVariant value; + if (!parseAnythingTo(&value)) goto ERROR_INVALID_VALUE; + if (!object.set(key, value)) goto ERROR_NO_MEMORY; // 3 - More keys/values? if (skip('}')) goto SUCCESS_NON_EMPTY_OBJECT; @@ -150,19 +155,31 @@ ERROR_INVALID_VALUE: ERROR_MISSING_BRACE: ERROR_MISSING_COLON: ERROR_MISSING_COMMA: +ERROR_NO_MEMORY: return JsonObject::invalid(); } -void JsonParser::parseBooleanTo(JsonVariant &destination) { - if (skip("true")) - destination = true; - else if (skip("false")) - destination = false; - else - destination = JsonVariant::invalid(); +bool JsonParser::parseObjectTo(JsonVariant *destination) { + JsonObject &object = parseObject(); + if (!object.success()) return false; + + *destination = object; + return true; } -void JsonParser::parseNumberTo(JsonVariant &destination) { +bool JsonParser::parseBooleanTo(JsonVariant *destination) { + if (skip("true")) { + *destination = true; + return true; + } else if (skip("false")) { + *destination = false; + return true; + } else { + return false; + } +} + +bool JsonParser::parseNumberTo(JsonVariant *destination) { char *endOfLong; long longValue = strtol(_ptr, &endOfLong, 10); char stopChar = *endOfLong; @@ -176,22 +193,28 @@ void JsonParser::parseNumberTo(JsonVariant &destination) { // Count the decimal digits uint8_t decimals = static_cast(_ptr - endOfLong - 1); // Set the variant as a double - destination.set(doubleValue, decimals); + *destination = JsonVariant(doubleValue, decimals); } else { // No => set the variant as a long _ptr = endOfLong; - destination = longValue; + *destination = longValue; } + return true; } -void JsonParser::parseNullTo(JsonVariant &destination) { +bool JsonParser::parseNullTo(JsonVariant *destination) { const char *NULL_STRING = NULL; - if (skip("null")) - destination = NULL_STRING; - else - destination = JsonVariant::invalid(); + if (!skip("null")) return false; + *destination = NULL_STRING; + return true; } const char *JsonParser::parseString() { return QuotedString::extractFrom(_ptr, &_ptr); } + +bool JsonParser::parseStringTo(JsonVariant *destination) { + const char *value = parseString(); + *destination = value; + return value != NULL; +} diff --git a/src/Internals/List.cpp b/src/Internals/List.cpp index c75a030a..00ed3efe 100644 --- a/src/Internals/List.cpp +++ b/src/Internals/List.cpp @@ -13,8 +13,8 @@ using namespace ArduinoJson; using namespace ArduinoJson::Internals; template -int List::size() const { - int nodeCount = 0; +size_t List::size() const { + size_t nodeCount = 0; for (node_type *node = _firstNode; node; node = node->next) nodeCount++; return nodeCount; } diff --git a/src/Internals/Prettyfier.cpp b/src/Internals/Prettyfier.cpp index 615c8ca3..b5ff4e45 100644 --- a/src/Internals/Prettyfier.cpp +++ b/src/Internals/Prettyfier.cpp @@ -33,7 +33,7 @@ inline size_t Prettyfier::handleMarkupChar(uint8_t c) { return handleBlockClose(c); case ':': - return handleColumn(); + return handleColon(); case ',': return handleComma(); @@ -54,7 +54,7 @@ inline size_t Prettyfier::handleBlockClose(uint8_t c) { return unindentIfNeeded() + _sink.write(c); } -inline size_t Prettyfier::handleColumn() { +inline size_t Prettyfier::handleColon() { return _sink.write(':') + _sink.write(' '); } diff --git a/src/JsonArray.cpp b/src/JsonArray.cpp index 65d3e126..e47d4290 100644 --- a/src/JsonArray.cpp +++ b/src/JsonArray.cpp @@ -14,18 +14,10 @@ using namespace ArduinoJson::Internals; JsonArray JsonArray::_invalid(NULL); -JsonVariant &JsonArray::at(int index) const { - node_type *node = getNodeAt(index); - return node ? node->content : JsonVariant::invalid(); -} - -JsonVariant &JsonArray::add() { - node_type *node = createNode(); - if (!node) return JsonVariant::invalid(); - - addNode(node); - - return node->content; +JsonArray::node_type *JsonArray::getNodeAt(size_t index) const { + node_type *node = _firstNode; + while (node && index--) node = node->next; + return node; } JsonArray &JsonArray::createNestedArray() { @@ -42,13 +34,7 @@ JsonObject &JsonArray::createNestedObject() { return object; } -JsonArray::node_type *JsonArray::getNodeAt(int index) const { - node_type *node = _firstNode; - while (node && index--) node = node->next; - return node; -} - -void JsonArray::removeAt(int index) { removeNode(getNodeAt(index)); } +void JsonArray::removeAt(size_t index) { removeNode(getNodeAt(index)); } void JsonArray::writeTo(JsonWriter &writer) const { writer.beginArray(); diff --git a/src/JsonObject.cpp b/src/JsonObject.cpp index 62ebf9c1..1cc29859 100644 --- a/src/JsonObject.cpp +++ b/src/JsonObject.cpp @@ -17,45 +17,25 @@ using namespace ArduinoJson::Internals; JsonObject JsonObject::_invalid(NULL); -JsonVariant &JsonObject::at(const char *key) { - node_type *node = getNodeAt(key); - return node ? node->content.value : JsonVariant::invalid(); +JsonObject::node_type *JsonObject::getOrCreateNodeAt(const char *key) { + node_type *existingNode = getNodeAt(key); + if (existingNode) return existingNode; + + node_type *newNode = addNewNode(); + return newNode; } -const JsonVariant &JsonObject::at(const char *key) const { - node_type *node = getNodeAt(key); - return node ? node->content.value : JsonVariant::invalid(); -} - -JsonVariant &JsonObject::operator[](const char *key) { - // try to find an existing node - node_type *node = getNodeAt(key); - - // not fount => create a new one - if (!node) { - node = createNode(); - if (!node) return JsonVariant::invalid(); - - node->content.key = key; - addNode(node); - } - - return node->content.value; -} - -void JsonObject::remove(char const *key) { removeNode(getNodeAt(key)); } - -JsonArray &JsonObject::createNestedArray(char const *key) { +JsonArray &JsonObject::createNestedArray(const char *key) { if (!_buffer) return JsonArray::invalid(); JsonArray &array = _buffer->createArray(); - add(key, array); + set(key, array); return array; } JsonObject &JsonObject::createNestedObject(const char *key) { if (!_buffer) return JsonObject::invalid(); JsonObject &object = _buffer->createObject(); - add(key, object); + set(key, object); return object; } diff --git a/src/JsonVariant.cpp b/src/JsonVariant.cpp index 0989a82d..92d8dd74 100644 --- a/src/JsonVariant.cpp +++ b/src/JsonVariant.cpp @@ -12,84 +12,6 @@ using namespace ArduinoJson; using namespace ArduinoJson::Internals; -JsonVariant JsonVariant::_invalid(JSON_INVALID); - -JsonVariant::operator JsonArray &() const { - return _type == JSON_ARRAY ? *_content.asArray : JsonArray::invalid(); -} - -JsonVariant::operator JsonObject &() const { - return _type == JSON_OBJECT ? *_content.asObject : JsonObject::invalid(); -} - -JsonVariant::operator bool() const { - return _type == JSON_BOOLEAN ? _content.asBoolean : false; -} - -JsonVariant::operator const char *() const { - return _type == JSON_STRING ? _content.asString : NULL; -} - -JsonVariant::operator double() const { - return _type >= JSON_DOUBLE_0_DECIMALS ? _content.asDouble : 0; -} - -JsonVariant::operator long() const { - return _type == JSON_LONG ? _content.asLong : 0; -} - -void JsonVariant::set(bool value) { - if (_type == JSON_INVALID) return; - _type = Internals::JSON_BOOLEAN; - _content.asBoolean = value; -} - -void JsonVariant::set(const char *value) { - if (_type == JSON_INVALID) return; - _type = JSON_STRING; - _content.asString = value; -} - -void JsonVariant::set(double value, uint8_t decimals) { - if (_type == JSON_INVALID) return; - _type = static_cast(JSON_DOUBLE_0_DECIMALS + decimals); - _content.asDouble = value; -} - -void JsonVariant::set(long value) { - if (_type == JSON_INVALID) return; - _type = JSON_LONG; - _content.asLong = value; -} - -void JsonVariant::set(JsonArray &array) { - if (_type == JSON_INVALID) return; - _type = array.success() ? JSON_ARRAY : JSON_INVALID; - _content.asArray = &array; -} - -void JsonVariant::set(JsonObject &object) { - if (_type == JSON_INVALID) return; - _type = object.success() ? JSON_OBJECT : JSON_INVALID; - _content.asObject = &object; -} - -size_t JsonVariant::size() const { - if (_type == JSON_ARRAY) return _content.asArray->size(); - if (_type == JSON_OBJECT) return _content.asObject->size(); - return 0; -} - -JsonVariant &JsonVariant::operator[](int index) { - if (_type != JSON_ARRAY) return JsonVariant::invalid(); - return _content.asArray->operator[](index); -} - -JsonVariant &JsonVariant::operator[](const char *key) { - if (_type != JSON_OBJECT) return JsonVariant::invalid(); - return _content.asObject->operator[](key); -} - void JsonVariant::writeTo(JsonWriter &writer) const { if (is()) as().writeTo(writer); diff --git a/test/DynamicJsonBuffer_Object_Tests.cpp b/test/DynamicJsonBuffer_Object_Tests.cpp index cb1e0ace..01d499b0 100644 --- a/test/DynamicJsonBuffer_Object_Tests.cpp +++ b/test/DynamicJsonBuffer_Object_Tests.cpp @@ -13,12 +13,12 @@ TEST(DynamicJsonBuffer_Object_Tests, GrowsWithObject) { JsonObject &obj = json.createObject(); ASSERT_EQ(JSON_OBJECT_SIZE(0), json.size()); - obj["hello"]; + obj["hello"] = 1; ASSERT_EQ(JSON_OBJECT_SIZE(1), json.size()); - obj["world"]; + obj["world"] = 2; ASSERT_EQ(JSON_OBJECT_SIZE(2), json.size()); - obj["world"]; // <- same value, should not grow + obj["world"] = 3; // <- same key, should not grow ASSERT_EQ(JSON_OBJECT_SIZE(2), json.size()); } diff --git a/test/GbathreeBug.cpp b/test/GbathreeBug.cpp index 414afc04..2cd6e886 100644 --- a/test/GbathreeBug.cpp +++ b/test/GbathreeBug.cpp @@ -37,7 +37,7 @@ class GbathreeBug : public testing::Test { TEST_F(GbathreeBug, Success) { EXPECT_TRUE(_object.success()); } TEST_F(GbathreeBug, ProtocolName) { - EXPECT_STREQ("fluorescence", _object.at("protocol_name").asString()); + EXPECT_STREQ("fluorescence", _object["protocol_name"]); } TEST_F(GbathreeBug, Repeats) { EXPECT_EQ(1, _object["repeats"]); } @@ -69,7 +69,7 @@ TEST_F(GbathreeBug, Calintensity) { EXPECT_EQ(255, _object["calintensity"]); } TEST_F(GbathreeBug, Pulses) { // "pulses":[50,50,50] - JsonArray& array = _object.at("pulses"); + JsonArray& array = _object["pulses"]; EXPECT_TRUE(array.success()); EXPECT_EQ(3, array.size()); @@ -82,7 +82,7 @@ TEST_F(GbathreeBug, Pulses) { TEST_F(GbathreeBug, Act) { // "act":[2,1,2,2] - JsonArray& array = _object.at("act"); + JsonArray& array = _object["act"]; EXPECT_TRUE(array.success()); EXPECT_EQ(4, array.size()); @@ -95,7 +95,7 @@ TEST_F(GbathreeBug, Act) { TEST_F(GbathreeBug, Detectors) { // "detectors":[[34,34,34,34],[34,34,34,34],[34,34,34,34],[34,34,34,34]] - JsonArray& array = _object.at("detectors"); + JsonArray& array = _object["detectors"]; EXPECT_TRUE(array.success()); EXPECT_EQ(4, array.size()); @@ -110,7 +110,7 @@ TEST_F(GbathreeBug, Detectors) { TEST_F(GbathreeBug, Alta) { // alta:[2,2,2,2] - JsonArray& array = _object.at("alta"); + JsonArray& array = _object["alta"]; EXPECT_TRUE(array.success()); EXPECT_EQ(4, array.size()); @@ -123,7 +123,7 @@ TEST_F(GbathreeBug, Alta) { TEST_F(GbathreeBug, Altb) { // altb:[2,2,2,2] - JsonArray& array = _object.at("altb"); + JsonArray& array = _object["altb"]; EXPECT_TRUE(array.success()); EXPECT_EQ(4, array.size()); @@ -136,7 +136,7 @@ TEST_F(GbathreeBug, Altb) { TEST_F(GbathreeBug, Measlights) { // "measlights":[[15,15,15,15],[15,15,15,15],[15,15,15,15],[15,15,15,15]] - JsonArray& array = _object.at("measlights"); + JsonArray& array = _object["measlights"]; EXPECT_TRUE(array.success()); EXPECT_EQ(4, array.size()); @@ -152,7 +152,7 @@ TEST_F(GbathreeBug, Measlights) { TEST_F(GbathreeBug, Measlights2) { // "measlights2":[[15,15,15,15],[15,15,15,15],[15,15,15,15],[15,15,15,15]] - JsonArray& array = _object.at("measlights2"); + JsonArray& array = _object["measlights2"]; EXPECT_TRUE(array.success()); EXPECT_EQ(4, array.size()); @@ -167,7 +167,7 @@ TEST_F(GbathreeBug, Measlights2) { TEST_F(GbathreeBug, Altc) { // altc:[2,2,2,2] - JsonArray& array = _object.at("altc"); + JsonArray& array = _object["altc"]; EXPECT_TRUE(array.success()); EXPECT_EQ(4, array.size()); @@ -180,7 +180,7 @@ TEST_F(GbathreeBug, Altc) { TEST_F(GbathreeBug, Altd) { // altd:[2,2,2,2] - JsonArray& array = _object.at("altd"); + JsonArray& array = _object["altd"]; EXPECT_TRUE(array.success()); EXPECT_EQ(4, array.size()); diff --git a/test/JsonArray_Container_Tests.cpp b/test/JsonArray_Container_Tests.cpp index 7b3a7fa1..a8c7558d 100644 --- a/test/JsonArray_Container_Tests.cpp +++ b/test/JsonArray_Container_Tests.cpp @@ -67,6 +67,12 @@ TEST_F(JsonArray_Container_Tests, Grow_WhenValuesAreAdded) { sizeMustBe(2); } +TEST_F(JsonArray_Container_Tests, DontGrow_WhenValuesAreReplaced) { + _array.add("hello"); + _array[0] = "world"; + sizeMustBe(1); +} + TEST_F(JsonArray_Container_Tests, CanStoreIntegers) { _array.add(123); _array.add(456); diff --git a/test/JsonArray_Invalid_Tests.cpp b/test/JsonArray_Invalid_Tests.cpp index 47b86c82..95886d37 100644 --- a/test/JsonArray_Invalid_Tests.cpp +++ b/test/JsonArray_Invalid_Tests.cpp @@ -7,10 +7,6 @@ #include #include -TEST(JsonArray_Invalid_Tests, AtFails) { - ASSERT_FALSE(JsonArray::invalid().at(0).success()); -} - TEST(JsonArray_Invalid_Tests, SubscriptFails) { ASSERT_FALSE(JsonArray::invalid()[0].success()); } @@ -33,4 +29,4 @@ TEST(JsonArray_Invalid_Tests, PrintToWritesBrackets) { char buffer[32]; JsonArray::invalid().printTo(buffer, sizeof(buffer)); ASSERT_STREQ("[]", buffer); -} \ No newline at end of file +} diff --git a/test/JsonArray_PrintTo_Tests.cpp b/test/JsonArray_PrintTo_Tests.cpp index 16715b4e..0afbafec 100644 --- a/test/JsonArray_PrintTo_Tests.cpp +++ b/test/JsonArray_PrintTo_Tests.cpp @@ -63,7 +63,7 @@ TEST_F(JsonArray_PrintTo_Tests, OneDoubleDefaultDigits) { } TEST_F(JsonArray_PrintTo_Tests, OneDoubleFourDigits) { - array.add(3.14159265358979323846, 4); + array.add(double_with_n_digits(3.14159265358979323846, 4)); outputMustBe("[3.1416]"); } diff --git a/test/JsonObject_Container_Tests.cpp b/test/JsonObject_Container_Tests.cpp index 6e6ea796..e1ee775a 100644 --- a/test/JsonObject_Container_Tests.cpp +++ b/test/JsonObject_Container_Tests.cpp @@ -21,24 +21,24 @@ TEST_F(JsonObject_Container_Tests, InitialSizeIsZero) { } TEST_F(JsonObject_Container_Tests, Grow_WhenValuesAreAdded) { - _object["hello"]; + _object["hello"] = 1; EXPECT_EQ(1, _object.size()); - _object["world"]; + _object.set("world", 2); EXPECT_EQ(2, _object.size()); } TEST_F(JsonObject_Container_Tests, DoNotGrow_WhenSameValueIsAdded) { - _object["hello"]; + _object["hello"] = 1; EXPECT_EQ(1, _object.size()); - _object["hello"]; + _object["hello"] = 2; EXPECT_EQ(1, _object.size()); } TEST_F(JsonObject_Container_Tests, Shrink_WhenValuesAreRemoved) { - _object["hello"]; - _object["world"]; + _object["hello"] = 1; + _object["world"] = 2; _object.remove("hello"); EXPECT_EQ(1, _object.size()); @@ -49,8 +49,8 @@ TEST_F(JsonObject_Container_Tests, Shrink_WhenValuesAreRemoved) { TEST_F(JsonObject_Container_Tests, DoNotShrink_WhenRemoveIsCalledWithAWrongKey) { - _object["hello"]; - _object["world"]; + _object["hello"] = 1; + _object["world"] = 2; _object.remove(":-P"); @@ -111,16 +111,11 @@ TEST_F(JsonObject_Container_Tests, CanStoreInnerObjects) { EXPECT_EQ(&innerObject2, &_object["world"].asObject()); } -TEST_F(JsonObject_Container_Tests, ContainsKeyReturnFalseForNonExistingKey) { +TEST_F(JsonObject_Container_Tests, ContainsKeyReturnsFalseForNonExistingKey) { EXPECT_FALSE(_object.containsKey("hello")); } -TEST_F(JsonObject_Container_Tests, ContainsKeyReturnTrueForDefinedValue) { - _object.add("hello", 42); +TEST_F(JsonObject_Container_Tests, ContainsKeyReturnsTrueForDefinedValue) { + _object.set("hello", 42); EXPECT_TRUE(_object.containsKey("hello")); } - -TEST_F(JsonObject_Container_Tests, ContainsKeyReturnFalseForUndefinedValue) { - _object.add("hello"); - EXPECT_FALSE(_object.containsKey("hello")); -} diff --git a/test/JsonObject_Invalid_Tests.cpp b/test/JsonObject_Invalid_Tests.cpp index 1cb1a305..28eb63d2 100644 --- a/test/JsonObject_Invalid_Tests.cpp +++ b/test/JsonObject_Invalid_Tests.cpp @@ -7,18 +7,14 @@ #include #include -TEST(JsonObject_Invalid_Tests, AtFails) { - ASSERT_FALSE(JsonObject::invalid().at(0).success()); -} - TEST(JsonObject_Invalid_Tests, SubscriptFails) { ASSERT_FALSE(JsonObject::invalid()[0].success()); } TEST(JsonObject_Invalid_Tests, AddFails) { - JsonObject& array = JsonObject::invalid(); - array.add("hello", "world"); - ASSERT_EQ(0, array.size()); + JsonObject& object = JsonObject::invalid(); + object.set("hello", "world"); + ASSERT_EQ(0, object.size()); } TEST(JsonObject_Invalid_Tests, CreateNestedArrayFails) { @@ -33,4 +29,4 @@ TEST(JsonObject_Invalid_Tests, PrintToWritesBraces) { char buffer[32]; JsonObject::invalid().printTo(buffer, sizeof(buffer)); ASSERT_STREQ("{}", buffer); -} \ No newline at end of file +} diff --git a/test/JsonObject_PrintTo_Tests.cpp b/test/JsonObject_PrintTo_Tests.cpp index 9c5a994c..f9057d4b 100644 --- a/test/JsonObject_PrintTo_Tests.cpp +++ b/test/JsonObject_PrintTo_Tests.cpp @@ -88,7 +88,7 @@ TEST_F(JsonObject_PrintTo_Tests, OneInteger) { } TEST_F(JsonObject_PrintTo_Tests, OneDoubleFourDigits) { - object["key"].set(3.14159265358979323846, 4); + object["key"] = double_with_n_digits(3.14159265358979323846, 4); outputMustBe("{\"key\":3.1416}"); } diff --git a/test/JsonParser_Array_Tests.cpp b/test/JsonParser_Array_Tests.cpp index d3eb2f6d..2d6fbe3b 100644 --- a/test/JsonParser_Array_Tests.cpp +++ b/test/JsonParser_Array_Tests.cpp @@ -35,11 +35,11 @@ class JsonParser_Array_Tests : public testing::Test { template void elementAtIndexMustBe(int index, T expected) { - EXPECT_EQ(expected, _array->at(index).as()); + EXPECT_EQ(expected, (*_array)[index].as()); } void elementAtIndexMustBe(int index, const char *expected) { - EXPECT_STREQ(expected, _array->at(index).as()); + EXPECT_STREQ(expected, (*_array)[index].as()); } DynamicJsonBuffer _jsonBuffer; diff --git a/test/JsonParser_Object_Tests.cpp b/test/JsonParser_Object_Tests.cpp index 0f183667..b868c68e 100644 --- a/test/JsonParser_Object_Tests.cpp +++ b/test/JsonParser_Object_Tests.cpp @@ -21,12 +21,12 @@ class JsonParser_Object_Test : public testing::Test { void sizeMustBe(int expected) { EXPECT_EQ(expected, _object->size()); } void keyMustHaveValue(const char *key, const char *expected) { - EXPECT_STREQ(expected, _object->at(key).as()); + EXPECT_STREQ(expected, (*_object)[key]); } template void keyMustHaveValue(const char *key, T expected) { - EXPECT_EQ(expected, _object->at(key).as()); + EXPECT_EQ(expected, (*_object)[key].as()); } private: diff --git a/test/JsonVariant_Invalid_Tests.cpp b/test/JsonVariant_Invalid_Tests.cpp deleted file mode 100644 index f18dae92..00000000 --- a/test/JsonVariant_Invalid_Tests.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright Benoit Blanchon 2014-2015 -// MIT License -// -// Arduino JSON library -// https://github.com/bblanchon/ArduinoJson - -#include -#include -#include "Printers.hpp" - -class JsonVariant_Invalid_Tests : public ::testing::Test { - public: - JsonVariant_Invalid_Tests() : variant(JsonVariant::invalid()) {} - - protected: - JsonVariant variant; -}; - -TEST_F(JsonVariant_Invalid_Tests, SuccessReturnsFalse) { - EXPECT_FALSE(variant.success()); -} - -TEST_F(JsonVariant_Invalid_Tests, AsLongReturns0) { - EXPECT_EQ(0, variant.as()); -} - -TEST_F(JsonVariant_Invalid_Tests, AsStringReturnsNull) { - EXPECT_EQ(0, variant.asString()); -} - -TEST_F(JsonVariant_Invalid_Tests, AsDoubleReturns0) { - EXPECT_EQ(0, variant.as()); -} - -TEST_F(JsonVariant_Invalid_Tests, AsBoolReturnsFalse) { - EXPECT_FALSE(variant.as()); -} - -TEST_F(JsonVariant_Invalid_Tests, AsArrayReturnInvalid) { - EXPECT_EQ(JsonArray::invalid(), variant.asArray()); -} - -TEST_F(JsonVariant_Invalid_Tests, AsObjectReturnInvalid) { - EXPECT_EQ(JsonObject::invalid(), variant.asObject()); -} - -TEST_F(JsonVariant_Invalid_Tests, CanNotBeSetToLong) { - variant = 0L; - EXPECT_FALSE(variant.success()); -} - -TEST_F(JsonVariant_Invalid_Tests, CanNotBeSetToDouble) { - variant = 0.0; - EXPECT_FALSE(variant.success()); -} - -TEST_F(JsonVariant_Invalid_Tests, CanNotBeSetToString) { - variant = static_cast(NULL); - EXPECT_FALSE(variant.success()); -} - -TEST_F(JsonVariant_Invalid_Tests, CanNotBeSetToBool) { - variant = false; - EXPECT_FALSE(variant.success()); -} - -TEST_F(JsonVariant_Invalid_Tests, CanNotBeSetToArray) { - variant = JsonArray::invalid(); - EXPECT_FALSE(variant.success()); -} - -TEST_F(JsonVariant_Invalid_Tests, CanNotBeSetToObject) { - variant = JsonObject::invalid(); - EXPECT_FALSE(variant.success()); -} diff --git a/test/JsonVariant_PrintTo_Tests.cpp b/test/JsonVariant_PrintTo_Tests.cpp index cd0bc5e8..635a209d 100644 --- a/test/JsonVariant_PrintTo_Tests.cpp +++ b/test/JsonVariant_PrintTo_Tests.cpp @@ -42,7 +42,7 @@ TEST_F(JsonVariant_PrintTo_Tests, DoubleDefaultDigits) { } TEST_F(JsonVariant_PrintTo_Tests, DoubleFourDigits) { - variant.set(3.14159265358979323846, 4); + variant = JsonVariant(3.14159265358979323846, 4); outputMustBe("3.1416"); } diff --git a/test/JsonVariant_Storage_Tests.cpp b/test/JsonVariant_Storage_Tests.cpp index eba73b71..b4fcf8ff 100644 --- a/test/JsonVariant_Storage_Tests.cpp +++ b/test/JsonVariant_Storage_Tests.cpp @@ -11,13 +11,13 @@ class JsonVariant_Storage_Tests : public ::testing::Test { protected: template void testValue(T expected) { - _actual.set(expected); + _actual = expected; EXPECT_EQ(expected, _actual.as()); } template void testReference(T &expected) { - _actual.set(expected); + _actual = expected; EXPECT_EQ(expected, _actual.as()); } diff --git a/test/JsonVariant_Subscript_Tests.cpp b/test/JsonVariant_Subscript_Tests.cpp index 46ab4998..15a68b86 100644 --- a/test/JsonVariant_Subscript_Tests.cpp +++ b/test/JsonVariant_Subscript_Tests.cpp @@ -49,13 +49,6 @@ TEST_F(JsonVariant_Subscript_Tests, Undefined) { EXPECT_FALSE(_variant[0].success()); } -TEST_F(JsonVariant_Subscript_Tests, Invalid) { - _variant = JsonVariant::invalid(); - EXPECT_EQ(0, _variant.size()); - EXPECT_FALSE(_variant["0"].success()); - EXPECT_FALSE(_variant[0].success()); -} - TEST_F(JsonVariant_Subscript_Tests, String) { _variant = "hello world"; EXPECT_EQ(0, _variant.size()); diff --git a/test/JsonVariant_Undefined_Tests.cpp b/test/JsonVariant_Undefined_Tests.cpp index 8b687cf5..5d1c7db3 100644 --- a/test/JsonVariant_Undefined_Tests.cpp +++ b/test/JsonVariant_Undefined_Tests.cpp @@ -13,10 +13,6 @@ class JsonVariant_Undefined_Tests : public ::testing::Test { JsonVariant variant; }; -TEST_F(JsonVariant_Undefined_Tests, SuccessReturnsFalse) { - EXPECT_FALSE(variant.success()); -} - TEST_F(JsonVariant_Undefined_Tests, AsLongReturns0) { EXPECT_EQ(0, variant.as()); } @@ -40,35 +36,3 @@ TEST_F(JsonVariant_Undefined_Tests, AsArrayReturnInvalid) { TEST_F(JsonVariant_Undefined_Tests, AsObjectReturnInvalid) { EXPECT_EQ(JsonObject::invalid(), variant.asObject()); } - -TEST_F(JsonVariant_Undefined_Tests, CanBeSetToLong) { - variant = 0L; - EXPECT_TRUE(variant.success()); -} - -TEST_F(JsonVariant_Undefined_Tests, CanBeSetToDouble) { - variant = 0.0; - EXPECT_TRUE(variant.success()); -} - -TEST_F(JsonVariant_Undefined_Tests, CanBeSetToString) { - variant = static_cast(NULL); - EXPECT_TRUE(variant.success()); -} - -TEST_F(JsonVariant_Undefined_Tests, CanBeSetToBool) { - variant = false; - EXPECT_TRUE(variant.success()); -} - -TEST_F(JsonVariant_Undefined_Tests, CanBeSetToArray) { - DynamicJsonBuffer jsonBuffer; - variant = jsonBuffer.createArray(); - EXPECT_TRUE(variant.success()); -} - -TEST_F(JsonVariant_Undefined_Tests, CanBeSetToObject) { - DynamicJsonBuffer jsonBuffer; - variant = jsonBuffer.createObject(); - EXPECT_TRUE(variant.success()); -} diff --git a/test/Printers.cpp b/test/Printers.cpp index e32aca29..8b7e7451 100644 --- a/test/Printers.cpp +++ b/test/Printers.cpp @@ -36,3 +36,15 @@ std::ostream& ArduinoJson::operator<<(std::ostream& os, v.printTo(adapter); return os; } + +std::ostream& ArduinoJson::operator<<( + std::ostream& os, const ArduinoJson::JsonObjectSubscript& v) { + JsonVariant value = v; + return os << value; +} + +std::ostream& ArduinoJson::operator<<( + std::ostream& os, const ArduinoJson::JsonArraySubscript& v) { + JsonVariant value = v; + return os << value; +} diff --git a/test/Printers.hpp b/test/Printers.hpp index b2cf3107..5abba06a 100644 --- a/test/Printers.hpp +++ b/test/Printers.hpp @@ -6,10 +6,12 @@ #pragma once -#include +#include #include namespace ArduinoJson { -std::ostream& operator<<(std::ostream& os, const ArduinoJson::JsonVariant& v); -std::ostream& operator<<(std::ostream& os, const ArduinoJson::JsonArray& v); +std::ostream& operator<<(std::ostream& os, const JsonVariant& v); +std::ostream& operator<<(std::ostream& os, const JsonArray& v); +std::ostream& operator<<(std::ostream& os, const JsonObjectSubscript& v); +std::ostream& operator<<(std::ostream& os, const JsonArraySubscript& v); } diff --git a/test/StaticJsonBuffer_CreateObject_Tests.cpp b/test/StaticJsonBuffer_CreateObject_Tests.cpp index decb73f1..3ddd0274 100644 --- a/test/StaticJsonBuffer_CreateObject_Tests.cpp +++ b/test/StaticJsonBuffer_CreateObject_Tests.cpp @@ -14,12 +14,15 @@ TEST(StaticJsonBuffer_CreateObject_Tests, GrowsWithObject) { ASSERT_EQ(JSON_OBJECT_SIZE(0), json.size()); obj["hello"]; + ASSERT_EQ(JSON_OBJECT_SIZE(0), json.size()); + + obj["hello"] = 1; ASSERT_EQ(JSON_OBJECT_SIZE(1), json.size()); - obj["world"]; + obj["world"] = 2; ASSERT_EQ(JSON_OBJECT_SIZE(2), json.size()); - obj["world"]; // <- same value, should not grow + obj["world"] = 3; // <- same key, should not grow ASSERT_EQ(JSON_OBJECT_SIZE(2), json.size()); } @@ -41,8 +44,8 @@ TEST(StaticJsonBuffer_CreateObject_Tests, ObjectDoesntGrowWhenFull) { StaticJsonBuffer json; JsonObject &obj = json.createObject(); - obj["hello"]; - obj["world"]; + obj["hello"] = 1; + obj["world"] = 2; ASSERT_EQ(JSON_OBJECT_SIZE(1), json.size()); }