Просмотр исходного кода

Redesigned JsonVariant to leverage converting constructors instead of assignment operators

Benoit Blanchon 10 лет назад
Родитель
Сommit
756c279cdc
44 измененных файлов с 931 добавлено и 646 удалено
  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());
 }