Browse Source

Redesigned JsonVariant to leverage converting constructors instead of assignment operators

Benoit Blanchon 10 năm trước cách đây
mục cha
commit
756c279cdc
44 tập tin đã thay đổi với 931 bổ sung646 xóa
  1. 12 2
      CHANGELOG.md
  2. 3 1
      README.md
  3. 2 2
      examples/JsonGeneratorExample/JsonGeneratorExample.ino
  4. 4 4
      include/ArduinoJson.h
  5. 13 0
      include/ArduinoJson/Internals/ForceInline.hpp
  6. 11 5
      include/ArduinoJson/Internals/JsonParser.hpp
  7. 5 0
      include/ArduinoJson/Internals/JsonVariantContent.hpp
  8. 96 0
      include/ArduinoJson/Internals/JsonVariantContent.ipp
  9. 3 1
      include/ArduinoJson/Internals/JsonVariantType.hpp
  10. 8 7
      include/ArduinoJson/Internals/List.hpp
  11. 1 1
      include/ArduinoJson/Internals/Prettyfier.hpp
  12. 26 27
      include/ArduinoJson/JsonArray.hpp
  13. 65 0
      include/ArduinoJson/JsonArray.ipp
  14. 40 0
      include/ArduinoJson/JsonArraySubscript.hpp
  15. 24 31
      include/ArduinoJson/JsonObject.hpp
  16. 69 0
      include/ArduinoJson/JsonObject.ipp
  17. 40 0
      include/ArduinoJson/JsonObjectSubscript.hpp
  18. 35 221
      include/ArduinoJson/JsonVariant.hpp
  19. 179 0
      include/ArduinoJson/JsonVariant.ipp
  20. 148 0
      include/ArduinoJson/JsonVariantBase.hpp
  21. 62 39
      src/Internals/JsonParser.cpp
  22. 2 2
      src/Internals/List.cpp
  23. 2 2
      src/Internals/Prettyfier.cpp
  24. 5 19
      src/JsonArray.cpp
  25. 8 28
      src/JsonObject.cpp
  26. 0 78
      src/JsonVariant.cpp
  27. 3 3
      test/DynamicJsonBuffer_Object_Tests.cpp
  28. 10 10
      test/GbathreeBug.cpp
  29. 6 0
      test/JsonArray_Container_Tests.cpp
  30. 1 5
      test/JsonArray_Invalid_Tests.cpp
  31. 1 1
      test/JsonArray_PrintTo_Tests.cpp
  32. 11 16
      test/JsonObject_Container_Tests.cpp
  33. 4 8
      test/JsonObject_Invalid_Tests.cpp
  34. 1 1
      test/JsonObject_PrintTo_Tests.cpp
  35. 2 2
      test/JsonParser_Array_Tests.cpp
  36. 2 2
      test/JsonParser_Object_Tests.cpp
  37. 0 75
      test/JsonVariant_Invalid_Tests.cpp
  38. 1 1
      test/JsonVariant_PrintTo_Tests.cpp
  39. 2 2
      test/JsonVariant_Storage_Tests.cpp
  40. 0 7
      test/JsonVariant_Subscript_Tests.cpp
  41. 0 36
      test/JsonVariant_Undefined_Tests.cpp
  42. 12 0
      test/Printers.cpp
  43. 5 3
      test/Printers.hpp
  44. 7 4
      test/StaticJsonBuffer_CreateObject_Tests.cpp

+ 12 - 2
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
 ----

+ 3 - 1
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
 

+ 2 - 2
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:

+ 4 - 4
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;

+ 13 - 0
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

+ 11 - 5
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;

+ 5 - 0
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 <typename T>
+  T as() const;
 };
 }
 }
+
+#include "JsonVariantContent.ipp"

+ 96 - 0
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<bool>() const {
+  return asBoolean;
+}
+
+template <>
+inline char const* JsonVariantContent::as<char const*>() const {
+  return asString;
+}
+
+template <>
+inline double JsonVariantContent::as<double>() const {
+  return asDouble;
+}
+
+template <>
+inline float JsonVariantContent::as<float>() const {
+  return static_cast<float>(asDouble);
+}
+
+template <>
+inline JsonArray& JsonVariantContent::as<JsonArray&>() const {
+  return *asArray;
+}
+
+template <>
+inline const JsonArray& JsonVariantContent::as<JsonArray const&>() const {
+  return *asArray;
+}
+
+template <>
+inline JsonObject& JsonVariantContent::as<JsonObject&>() const {
+  return *asObject;
+}
+
+template <>
+inline const JsonObject& JsonVariantContent::as<JsonObject const&>() const {
+  return *asObject;
+}
+
+template <>
+inline signed char JsonVariantContent::as<signed char>() const {
+  return static_cast<signed char>(asLong);
+}
+
+template <>
+inline signed int JsonVariantContent::as<signed int>() const {
+  return static_cast<signed int>(asLong);
+}
+
+template <>
+inline signed long JsonVariantContent::as<signed long>() const {
+  return static_cast<signed long>(asLong);
+}
+
+template <>
+inline signed short JsonVariantContent::as<signed short>() const {
+  return static_cast<signed short>(asLong);
+}
+
+template <>
+inline unsigned char JsonVariantContent::as<unsigned char>() const {
+  return static_cast<unsigned char>(asLong);
+}
+
+template <>
+inline unsigned int JsonVariantContent::as<unsigned int>() const {
+  return static_cast<unsigned int>(asLong);
+}
+
+template <>
+inline unsigned long JsonVariantContent::as<unsigned long>() const {
+  return static_cast<unsigned long>(asLong);
+}
+
+template <>
+inline unsigned short JsonVariantContent::as<unsigned short>() const {
+  return static_cast<unsigned short>(asLong);
+}
+}
+}

+ 3 - 1
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

+ 8 - 7
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);

+ 1 - 1
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);

+ 26 - 27
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<JsonArray>,
                   public Internals::ReferenceType,
                   public Internals::List<JsonVariant>,
                   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<JsonVariant>(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.
-  template <typename T>
-  void add(T value) {
-    add().set(value);
-  }
+  JSON_FORCE_INLINE bool add(const JsonVariant value);
 
-  // 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); }
+  // Sets the value at specified index.
+  JSON_FORCE_INLINE void set(size_t index, const JsonVariant value);
 
-  // Adds a reference to the specified JsonArray at the end of the array.
-  void add(JsonArray &array) { add().set(array); }
+  // Gets the value at the specified index.
+  JSON_FORCE_INLINE JsonVariant get(size_t index) const;
 
-  // Adds a reference to the specified JsonObject at the end of the array.
-  void add(JsonObject &obejct) { add().set(obejct); }
+  // Gets the value at the specified index.
+  template <typename T>
+  JSON_FORCE_INLINE T get(size_t index) const;
+
+  // Check the type of the value at specified index.
+  template <typename T>
+  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<JsonArray>,
   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<JsonArray>,
   void writeTo(Internals::JsonWriter &writer) const;
 
  private:
-  // Create an empty JsonArray attached to the specified JsonBuffer.
-  explicit JsonArray(JsonBuffer *buffer)
-      : Internals::List<JsonVariant>(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"

+ 65 - 0
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<JsonArray *>(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 <typename T>
+inline T JsonArray::get(size_t index) const {
+  node_type *node = getNodeAt(index);
+  return node ? node->content.as<T>() : JsonVariant::invalid<T>();
+}
+
+template <typename T>
+inline T JsonArray::is(size_t index) const {
+  node_type *node = getNodeAt(index);
+  return node ? node->content.is<T>() : false;
+}
+
+inline void JsonArray::set(size_t index, const JsonVariant value) {
+  node_type *node = getNodeAt(index);
+  if (node) node->content = value;
+}
+
+template <typename TImplem>
+inline const JsonArraySubscript JsonVariantBase<TImplem>::operator[](
+    int index) const {
+  return asArray()[index];
+}
+
+template <>
+inline JsonArray &JsonVariant::invalid<JsonArray &>() {
+  return JsonArray::invalid();
+}
+
+template <>
+inline JsonArray const &JsonVariant::invalid<JsonArray const &>() {
+  return JsonArray::invalid();
+}
+}

+ 40 - 0
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<JsonArraySubscript> {
+ 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 <typename T>
+  JSON_FORCE_INLINE T as() const {
+    return _array.get<T>(_index);
+  }
+
+  template <typename T>
+  JSON_FORCE_INLINE T is() const {
+    return _array.is<T>(_index);
+  }
+
+ private:
+  JsonArray& _array;
+  const size_t _index;
+};
+}

+ 24 - 31
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<JsonObject>,
                    public Internals::ReferenceType,
                    public Internals::List<JsonPair>,
                    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<JsonPair>(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 <typename T>
-  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 <typename T>
+  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>,
   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<JsonObject>,
   void writeTo(Internals::JsonWriter &writer) const;
 
  private:
-  // Create an empty JsonArray attached to the specified JsonBuffer.
-  explicit JsonObject(JsonBuffer *buffer) : Internals::List<JsonPair>(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"

+ 69 - 0
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 <typename T>
+inline T JsonObject::get(key_type key) const {
+  node_type *node = getNodeAt(key);
+  return node ? node->content.value.as<T>() : JsonVariant::invalid<T>();
+}
+
+template <typename T>
+inline T JsonObject::is(key_type key) const {
+  node_type *node = getNodeAt(key);
+  return node ? node->content.value.is<T>() : 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<JsonObject *>(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 <typename TImplem>
+inline const JsonObjectSubscript JsonVariantBase<TImplem>::operator[](
+    const char *key) const {
+  return asObject()[key];
+}
+
+template <>
+inline JsonObject const &JsonVariant::invalid<JsonObject const &>() {
+  return JsonObject::invalid();
+}
+
+template <>
+inline JsonObject &JsonVariant::invalid<JsonObject &>() {
+  return JsonObject::invalid();
+}
+}

+ 40 - 0
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<JsonObjectSubscript> {
+ 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 <typename T>
+  JSON_FORCE_INLINE T as() const {
+    return _object.get<T>(_key);
+  }
+
+  template <typename T>
+  JSON_FORCE_INLINE T is() const {
+    return _object.is<T>(_key);
+  }
+
+ private:
+  JsonObject& _object;
+  const char* _key;
+};
+}

+ 35 - 221
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<JsonVariant> {
+class JsonVariant : public Internals::JsonPrintable<JsonVariant>,
+                    public JsonVariantBase<JsonVariant> {
  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 <typename T>
-  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);
-
-  // Sets the variant to be an integer value.
-  void set(signed long value);
-  void set(signed char value) { set(static_cast<long>(value)); }
-  void set(signed int value) { set(static_cast<long>(value)); }
-  void set(signed short value) { set(static_cast<long>(value)); }
-  void set(unsigned char value) { set(static_cast<long>(value)); }
-  void set(unsigned int value) { set(static_cast<long>(value)); }
-  void set(unsigned long value) { set(static_cast<long>(value)); }
-  void set(unsigned short value) { set(static_cast<long>(value)); }
-
-  // Sets the variant to be a string.
-  void set(const char *value);
-
-  // Sets the variant to be a reference to an array.
-  void set(JsonArray &array);
-
-  // Sets the variant to be a reference to an object.
-  void set(JsonObject &object);
+  JSON_FORCE_INLINE JsonVariant(float value, uint8_t decimals = 2);
+  JSON_FORCE_INLINE JsonVariant(double value, uint8_t decimals = 2);
 
-  // Sets the variant to the specified value.
-  template <typename T>
-  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;
+  // 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);
 
-  // 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<float>(as<double>()); }
+  // Create a JsonVariant containing a string.
+  JSON_FORCE_INLINE JsonVariant(const char *value);
 
-  // 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<signed char>(); }
-  operator signed int() const { return cast_long_to<signed int>(); }
-  operator signed short() const { return cast_long_to<signed short>(); }
-  operator unsigned char() const { return cast_long_to<unsigned char>(); }
-  operator unsigned int() const { return cast_long_to<unsigned int>(); }
-  operator unsigned long() const { return cast_long_to<unsigned long>(); }
-  operator unsigned short() const { return cast_long_to<unsigned short>(); }
+  // Create a JsonVariant containing a reference to an array.
+  JSON_FORCE_INLINE JsonVariant(JsonArray &array);
 
-  // Gets the variant as a string.
-  // Returns NULL if variant is not a string.
-  operator const char *() const;
-  const char *asString() const { return as<const char *>(); }
-
-  // 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<JsonArray &>(); }
-
-  // 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<JsonObject &>(); }
+  // 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 <typename T>
-  T as() const {
-    return static_cast<T>(*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 <typename T>
-  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);
-
- private:
-  // Special constructor used only to create _invalid.
-  explicit JsonVariant(Internals::JsonVariantType type) : _type(type) {}
-
-  // Helper for interger cast operators
+  // TODO: rename
   template <typename T>
-  T cast_long_to() const {
-    return static_cast<T>(as<long>());
-  }
+  static T invalid();
 
+ private:
   // 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<long>() const {
-  return _type == Internals::JSON_LONG;
-}
-
-template <>
-inline bool JsonVariant::is<double>() const {
-  return _type >= Internals::JSON_DOUBLE_0_DECIMALS;
-}
-
-template <>
-inline bool JsonVariant::is<bool>() const {
-  return _type == Internals::JSON_BOOLEAN;
-}
-
-template <>
-inline bool JsonVariant::is<const char *>() const {
-  return _type == Internals::JSON_STRING;
-}
-
-template <>
-inline bool JsonVariant::is<JsonArray &>() const {
-  return _type == Internals::JSON_ARRAY;
-}
-
-template <>
-inline bool JsonVariant::is<const JsonArray &>() const {
-  return _type == Internals::JSON_ARRAY;
-}
-
-template <>
-inline bool JsonVariant::is<JsonObject &>() const {
-  return _type == Internals::JSON_OBJECT;
-}
-
-template <>
-inline bool JsonVariant::is<const JsonObject &>() const {
-  return _type == Internals::JSON_OBJECT;
-}
-
-template <typename T>
-inline bool operator==(const JsonVariant &left, T right) {
-  return left.as<T>() == right;
-}
-
-template <typename T>
-inline bool operator==(T left, const JsonVariant &right) {
-  return left == right.as<T>();
-}
-
-template <typename T>
-inline bool operator!=(const JsonVariant &left, T right) {
-  return left.as<T>() != right;
-}
-
-template <typename T>
-inline bool operator!=(T left, const JsonVariant &right) {
-  return left != right.as<T>();
-}
-
-template <typename T>
-inline bool operator<=(const JsonVariant &left, T right) {
-  return left.as<T>() <= right;
-}
-
-template <typename T>
-inline bool operator<=(T left, const JsonVariant &right) {
-  return left <= right.as<T>();
-}
-
-template <typename T>
-inline bool operator>=(const JsonVariant &left, T right) {
-  return left.as<T>() >= right;
+inline JsonVariant float_with_n_digits(float value, uint8_t digits) {
+  return JsonVariant(value, digits);
 }
 
-template <typename T>
-inline bool operator>=(T left, const JsonVariant &right) {
-  return left >= right.as<T>();
+inline JsonVariant double_with_n_digits(double value, uint8_t digits) {
+  return JsonVariant(value, digits);
 }
-
-template <typename T>
-inline bool operator<(const JsonVariant &left, T right) {
-  return left.as<T>() < right;
-}
-
-template <typename T>
-inline bool operator<(T left, const JsonVariant &right) {
-  return left < right.as<T>();
 }
 
-template <typename T>
-inline bool operator>(const JsonVariant &left, T right) {
-  return left.as<T>() > right;
-}
-
-template <typename T>
-inline bool operator>(T left, const JsonVariant &right) {
-  return left > right.as<T>();
-}
-}
+// Include inline implementations
+#include "JsonVariant.ipp"

+ 179 - 0
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::JsonVariantType>(
+      Internals::JSON_DOUBLE_0_DECIMALS + decimals);
+  _content.asDouble = value;
+}
+
+inline JsonVariant::JsonVariant(float value, uint8_t decimals) {
+  _type = static_cast<Internals::JsonVariantType>(
+      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 <typename T>
+inline T JsonVariant::as() const {
+  return is<T>() ? _content.as<T>() : invalid<T>();
+}
+
+template <typename T>
+inline T JsonVariant::invalid() {
+  return T();
+}
+
+template <typename T>
+inline bool JsonVariant::is() const {
+  return false;
+}
+
+template <>
+inline bool JsonVariant::is<bool>() const {
+  return _type == Internals::JSON_BOOLEAN;
+}
+
+template <>
+inline bool JsonVariant::is<char const *>() const {
+  return _type == Internals::JSON_STRING;
+}
+
+template <>
+inline bool JsonVariant::is<double>() const {
+  return _type >= Internals::JSON_DOUBLE_0_DECIMALS;
+}
+
+template <>
+inline bool JsonVariant::is<float>() const {
+  return _type >= Internals::JSON_DOUBLE_0_DECIMALS;
+}
+
+template <>
+inline bool JsonVariant::is<JsonArray &>() const {
+  return _type == Internals::JSON_ARRAY;
+}
+
+template <>
+inline bool JsonVariant::is<JsonArray const &>() const {
+  return _type == Internals::JSON_ARRAY;
+}
+
+template <>
+inline bool JsonVariant::is<JsonObject &>() const {
+  return _type == Internals::JSON_OBJECT;
+}
+
+template <>
+inline bool JsonVariant::is<JsonObject const &>() const {
+  return _type == Internals::JSON_OBJECT;
+}
+
+template <>
+inline bool JsonVariant::is<signed char>() const {
+  return _type == Internals::JSON_LONG;
+}
+
+template <>
+inline bool JsonVariant::is<signed int>() const {
+  return _type == Internals::JSON_LONG;
+}
+
+template <>
+inline bool JsonVariant::is<signed long>() const {
+  return _type == Internals::JSON_LONG;
+}
+
+template <>
+inline bool JsonVariant::is<signed short>() const {
+  return _type == Internals::JSON_LONG;
+}
+
+template <>
+inline bool JsonVariant::is<unsigned char>() const {
+  return _type == Internals::JSON_LONG;
+}
+
+template <>
+inline bool JsonVariant::is<unsigned int>() const {
+  return _type == Internals::JSON_LONG;
+}
+
+template <>
+inline bool JsonVariant::is<unsigned long>() const {
+  return _type == Internals::JSON_LONG;
+}
+
+template <>
+inline bool JsonVariant::is<unsigned short>() const {
+  return _type == Internals::JSON_LONG;
+}
+}

+ 148 - 0
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 <typename TImpl>
+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<bool>(); }
+
+  // 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<double>(); }
+  JSON_FORCE_INLINE operator float() const { return as<float>(); }
+
+  // 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<signed long>(); }
+  JSON_FORCE_INLINE operator signed char() const { return as<signed char>(); }
+  JSON_FORCE_INLINE operator signed int() const { return as<signed int>(); }
+  JSON_FORCE_INLINE operator signed short() const { return as<signed short>(); }
+  JSON_FORCE_INLINE operator unsigned char() const {
+    return as<unsigned char>();
+  }
+  JSON_FORCE_INLINE operator unsigned int() const { return as<unsigned int>(); }
+  JSON_FORCE_INLINE operator unsigned long() const {
+    return as<unsigned long>();
+  }
+  JSON_FORCE_INLINE operator unsigned short() const {
+    return as<unsigned short>();
+  }
+
+  // Gets the variant as a string.
+  // Returns NULL if variant is not a string.
+  JSON_FORCE_INLINE operator const char *() const { return as<const char *>(); }
+  JSON_FORCE_INLINE const char *asString() const { return as<const char *>(); }
+
+  // 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<JsonArray &>(); }
+  JSON_FORCE_INLINE JsonArray &asArray() const { return as<JsonArray &>(); }
+
+  // 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<JsonObject &>(); }
+  JSON_FORCE_INLINE JsonObject &asObject() const { return as<JsonObject &>(); }
+
+  template <typename T>
+  JSON_FORCE_INLINE const T as() const {
+    return impl()->template as<T>();
+  }
+
+  // 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<const TImpl *>(this); }
+};
+
+template <typename TImpl, typename TComparand>
+inline bool operator==(const JsonVariantBase<TImpl> &left, TComparand right) {
+  return left.template as<TComparand>() == right;
+}
+
+template <typename TImpl, typename TComparand>
+inline bool operator==(TComparand left, const JsonVariantBase<TImpl> &right) {
+  return left == right.template as<TComparand>();
+}
+
+template <typename TImpl, typename TComparand>
+inline bool operator!=(const JsonVariantBase<TImpl> &left, TComparand right) {
+  return left.template as<TComparand>() != right;
+}
+
+template <typename TImpl, typename TComparand>
+inline bool operator!=(TComparand left, const JsonVariantBase<TImpl> &right) {
+  return left != right.template as<TComparand>();
+}
+
+template <typename TImpl, typename TComparand>
+inline bool operator<=(const JsonVariantBase<TImpl> &left, TComparand right) {
+  return left.template as<TComparand>() <= right;
+}
+
+template <typename TImpl, typename TComparand>
+inline bool operator<=(TComparand left, const JsonVariantBase<TImpl> &right) {
+  return left <= right.template as<TComparand>();
+}
+
+template <typename TImpl, typename TComparand>
+inline bool operator>=(const JsonVariantBase<TImpl> &left, TComparand right) {
+  return left.template as<TComparand>() >= right;
+}
+
+template <typename TImpl, typename TComparand>
+inline bool operator>=(TComparand left, const JsonVariantBase<TImpl> &right) {
+  return left >= right.template as<TComparand>();
+}
+
+template <typename TImpl, typename TComparand>
+inline bool operator<(const JsonVariantBase<TImpl> &left, TComparand right) {
+  return left.template as<TComparand>() < right;
+}
+
+template <typename TImpl, typename TComparand>
+inline bool operator<(TComparand left, const JsonVariantBase<TImpl> &right) {
+  return left < right.template as<TComparand>();
+}
+
+template <typename TImpl, typename TComparand>
+inline bool operator>(const JsonVariantBase<TImpl> &left, TComparand right) {
+  return left.template as<TComparand>() > right;
+}
+
+template <typename TImpl, typename TComparand>
+inline bool operator>(TComparand left, const JsonVariantBase<TImpl> &right) {
+  return left > right.template as<TComparand>();
+}
+}

+ 62 - 39
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;
+}
+
+bool JsonParser::parseBooleanTo(JsonVariant *destination) {
+  if (skip("true")) {
+    *destination = true;
+    return true;
+  } else if (skip("false")) {
+    *destination = false;
+    return true;
+  } else {
+    return false;
+  }
 }
 
-void JsonParser::parseNumberTo(JsonVariant &destination) {
+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<uint8_t>(_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;
+}

+ 2 - 2
src/Internals/List.cpp

@@ -13,8 +13,8 @@ using namespace ArduinoJson;
 using namespace ArduinoJson::Internals;
 
 template <typename T>
-int List<T>::size() const {
-  int nodeCount = 0;
+size_t List<T>::size() const {
+  size_t nodeCount = 0;
   for (node_type *node = _firstNode; node; node = node->next) nodeCount++;
   return nodeCount;
 }

+ 2 - 2
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(' ');
 }
 

+ 5 - 19
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();

+ 8 - 28
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();
-}
-
-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);
+JsonObject::node_type *JsonObject::getOrCreateNodeAt(const char *key) {
+  node_type *existingNode = getNodeAt(key);
+  if (existingNode) return existingNode;
 
-  // 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;
+  node_type *newNode = addNewNode();
+  return newNode;
 }
 
-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;
 }
 

+ 0 - 78
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<JsonVariantType>(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<const JsonArray &>())
     as<const JsonArray &>().writeTo(writer);

+ 3 - 3
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());
 }

+ 10 - 10
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());

+ 6 - 0
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);

+ 1 - 5
test/JsonArray_Invalid_Tests.cpp

@@ -7,10 +7,6 @@
 #include <gtest/gtest.h>
 #include <ArduinoJson.h>
 
-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);
-}
+}

+ 1 - 1
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]");
 }
 

+ 11 - 16
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"));
-}

+ 4 - 8
test/JsonObject_Invalid_Tests.cpp

@@ -7,18 +7,14 @@
 #include <gtest/gtest.h>
 #include <ArduinoJson.h>
 
-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);
-}
+}

+ 1 - 1
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}");
 }
 

+ 2 - 2
test/JsonParser_Array_Tests.cpp

@@ -35,11 +35,11 @@ class JsonParser_Array_Tests : public testing::Test {
 
   template <typename T>
   void elementAtIndexMustBe(int index, T expected) {
-    EXPECT_EQ(expected, _array->at(index).as<T>());
+    EXPECT_EQ(expected, (*_array)[index].as<T>());
   }
 
   void elementAtIndexMustBe(int index, const char *expected) {
-    EXPECT_STREQ(expected, _array->at(index).as<const char *>());
+    EXPECT_STREQ(expected, (*_array)[index].as<const char *>());
   }
 
   DynamicJsonBuffer _jsonBuffer;

+ 2 - 2
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<const char *>());
+    EXPECT_STREQ(expected, (*_object)[key]);
   }
 
   template <typename T>
   void keyMustHaveValue(const char *key, T expected) {
-    EXPECT_EQ(expected, _object->at(key).as<T>());
+    EXPECT_EQ(expected, (*_object)[key].as<T>());
   }
 
  private:

+ 0 - 75
test/JsonVariant_Invalid_Tests.cpp

@@ -1,75 +0,0 @@
-// Copyright Benoit Blanchon 2014-2015
-// MIT License
-//
-// Arduino JSON library
-// https://github.com/bblanchon/ArduinoJson
-
-#include <gtest/gtest.h>
-#include <ArduinoJson.h>
-#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<long>());
-}
-
-TEST_F(JsonVariant_Invalid_Tests, AsStringReturnsNull) {
-  EXPECT_EQ(0, variant.asString());
-}
-
-TEST_F(JsonVariant_Invalid_Tests, AsDoubleReturns0) {
-  EXPECT_EQ(0, variant.as<double>());
-}
-
-TEST_F(JsonVariant_Invalid_Tests, AsBoolReturnsFalse) {
-  EXPECT_FALSE(variant.as<bool>());
-}
-
-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<const char*>(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());
-}

+ 1 - 1
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");
 }
 

+ 2 - 2
test/JsonVariant_Storage_Tests.cpp

@@ -11,13 +11,13 @@ class JsonVariant_Storage_Tests : public ::testing::Test {
  protected:
   template <typename T>
   void testValue(T expected) {
-    _actual.set(expected);
+    _actual = expected;
     EXPECT_EQ(expected, _actual.as<T>());
   }
 
   template <typename T>
   void testReference(T &expected) {
-    _actual.set(expected);
+    _actual = expected;
     EXPECT_EQ(expected, _actual.as<T &>());
   }
 

+ 0 - 7
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());

+ 0 - 36
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<long>());
 }
@@ -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<const char*>(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());
-}

+ 12 - 0
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;
+}

+ 5 - 3
test/Printers.hpp

@@ -6,10 +6,12 @@
 
 #pragma once
 
-#include <ArduinoJson/JsonVariant.hpp>
+#include <ArduinoJson.h>
 #include <ostream>
 
 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);
 }

+ 7 - 4
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_OBJECT_SIZE(1)> json;
 
   JsonObject &obj = json.createObject();
-  obj["hello"];
-  obj["world"];
+  obj["hello"] = 1;
+  obj["world"] = 2;
 
   ASSERT_EQ(JSON_OBJECT_SIZE(1), json.size());
 }