Ver Fonte

Templatized all functions using `String` or `std::string`
* Removed `ArduinoJson::String`
* Removed `JsonVariant::defaultValue<T>()`
* Removed non-template `JsonObject::get()` and `JsonArray.get()`
* Fixed support for `StringSumHelper` (issue #184)
* Replaced `ARDUINOJSON_USE_ARDUINO_STRING` by `ARDUINOJSON_ENABLE_STD_STRING` and `ARDUINOJSON_ENABLE_ARDUINO_STRING` (issue #378)
* Added example `StringExample.ino` to show where `String` can be used

Benoit Blanchon há 9 anos atrás
pai
commit
aa2ef79e55
31 ficheiros alterados com 622 adições e 545 exclusões
  1. 29 0
      CHANGELOG.md
  2. 53 0
      examples/StringExample/StringExample.ino
  3. 16 6
      include/ArduinoJson/Configuration.hpp
  4. 5 5
      include/ArduinoJson/Internals/DynamicStringBuilder.hpp
  5. 9 4
      include/ArduinoJson/Internals/JsonPrintable.hpp
  6. 9 7
      include/ArduinoJson/Internals/JsonVariantDefault.hpp
  7. 94 0
      include/ArduinoJson/Internals/StringFuncs.hpp
  8. 41 0
      include/ArduinoJson/Internals/ValueSetter.hpp
  9. 40 61
      include/ArduinoJson/JsonArray.hpp
  10. 13 31
      include/ArduinoJson/JsonArray.ipp
  11. 5 19
      include/ArduinoJson/JsonArraySubscript.hpp
  12. 20 49
      include/ArduinoJson/JsonBuffer.hpp
  13. 0 8
      include/ArduinoJson/JsonBuffer.ipp
  14. 59 73
      include/ArduinoJson/JsonObject.hpp
  15. 11 35
      include/ArduinoJson/JsonObject.ipp
  16. 0 27
      include/ArduinoJson/JsonObjectKey.hpp
  17. 29 54
      include/ArduinoJson/JsonObjectSubscript.hpp
  18. 0 1
      include/ArduinoJson/JsonPair.hpp
  19. 23 44
      include/ArduinoJson/JsonVariant.hpp
  20. 2 13
      include/ArduinoJson/JsonVariant.ipp
  21. 3 5
      include/ArduinoJson/JsonVariantBase.hpp
  22. 3 1
      include/ArduinoJson/RawJson.hpp
  23. 0 24
      include/ArduinoJson/String.hpp
  24. 31 0
      include/ArduinoJson/TypeTraits/ConstRefOrConstPtr.hpp
  25. 5 2
      include/ArduinoJson/TypeTraits/IsIntegral.hpp
  26. 14 1
      test/DynamicJsonBuffer_Basic_Tests.cpp
  27. 26 0
      test/JsonObject_Get_Tests.cpp
  28. 2 2
      test/JsonObject_Invalid_Tests.cpp
  29. 3 3
      test/JsonObject_Set_Tests.cpp
  30. 8 8
      test/JsonVariant_As_Tests.cpp
  31. 69 62
      test/String_Tests.cpp

+ 29 - 0
CHANGELOG.md

@@ -4,8 +4,37 @@ ArduinoJson: change log
 HEAD
 ----
 
+* Templatized all functions using `String` or `std::string`
+* Removed `ArduinoJson::String`
+* Removed `JsonVariant::defaultValue<T>()`
+* Removed non-template `JsonObject::get()` and `JsonArray.get()`
+* Fixed support for `StringSumHelper` (issue #184)
+* Replaced `ARDUINOJSON_USE_ARDUINO_STRING` by `ARDUINOJSON_ENABLE_STD_STRING` and `ARDUINOJSON_ENABLE_ARDUINO_STRING` (issue #378)
+* Added example `StringExample.ino` to show where `String` can be used
 * Increased default nesting limit to 50 when compiled for a computer (issue #349)
 
+**BREAKING CHANGES**:
+
+The non-template functions `JsonObject::get()` and `JsonArray.get()` have been removed. This means that you need to explicitely tell the type you expect in return.
+
+Old code:
+
+```c++
+#define ARDUINOJSON_USE_ARDUINO_STRING 0
+JsonVariant value1 = myObject.get("myKey");
+JsonVariant value2 = myArray.get(0);
+```
+
+New code:
+
+```c++
+#define ARDUINOJSON_ENABLE_ARDUINO_STRING 0
+#define ARDUINOJSON_ENABLE_STD_STRING 1
+JsonVariant value1 = myObject.get<JsonVariant>("myKey");
+JsonVariant value2 = myArray.get<JsonVariant>(0);
+```
+
+
 v5.6.7
 ------
 

+ 53 - 0
examples/StringExample/StringExample.ino

@@ -0,0 +1,53 @@
+// Copyright Benoit Blanchon 2014-2016
+// MIT License
+//
+// Arduino JSON library
+// https://github.com/bblanchon/ArduinoJson
+// If you like this project, please add a star!
+
+#include <ArduinoJson.h>
+
+// About
+// -----
+// This example shows the different ways you can use String with ArduinoJson.
+// Please don't see this as an invitation to use String.
+// On the contrary, you should always use char[] when possible, it's much more
+// efficient in term of code size, speed and memory usage.
+
+void setup() {
+  DynamicJsonBuffer jsonBuffer;
+
+  // You can use a String as your JSON input.
+  // WARNING: the content of the String will be duplicated in the JsonBuffer.
+  String input =
+      "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}";
+  JsonObject& root = jsonBuffer.parseObject(input);
+
+  // You can use a String to get an element of a JsonObject
+  // No duplication is done.
+  long time = root[String("time")];
+
+  // You can use a String to set an element of a JsonObject
+  // WARNING: the content of the String will be duplicated in the JsonBuffer.
+  root[String("time")] = time;
+
+  // You can get a String from a JsonObject or JsonArray:
+  // No duplication is done, at least not in the JsonBuffer.
+  String sensor = root[String("sensor")];
+
+  // You can set a String to a JsonObject or JsonArray:
+  // WARNING: the content of the String will be duplicated in the JsonBuffer.
+  root["sensor"] = sensor;
+
+  // You can also concatenate strings
+  // WARNING: the content of the String will be duplicated in the JsonBuffer.
+  root[String("sen") + "sor"] = String("gp") + "s";
+
+  // Lastly, you can print the resulting JSON to a String
+  String output;
+  root.printTo(output);
+}
+
+void loop() {
+  // not used in this example
+}

+ 16 - 6
include/ArduinoJson/Configuration.hpp

@@ -22,12 +22,17 @@
 #define ARDUINOJSON_USE_INT64 0
 #endif
 
-// arduino has its own implementation of String to replace std::string
-#ifndef ARDUINOJSON_USE_ARDUINO_STRING
-#define ARDUINOJSON_USE_ARDUINO_STRING 1
+// Arduino has its own implementation of String to replace std::string
+#ifndef ARDUINOJSON_ENABLE_ARDUINO_STRING
+#define ARDUINOJSON_ENABLE_ARDUINO_STRING 1
 #endif
 
-// arduino doesn't support STL stream
+// Arduino doesn't have std::string
+#ifndef ARDUINOJSON_ENABLE_STD_STRING
+#define ARDUINOJSON_ENABLE_STD_STRING 0
+#endif
+
+// Arduino doesn't support STL stream
 #ifndef ARDUINOJSON_ENABLE_STD_STREAM
 #define ARDUINOJSON_ENABLE_STD_STREAM 0
 #endif
@@ -73,8 +78,13 @@
 #endif
 
 // on a computer, we can use std::string
-#ifndef ARDUINOJSON_USE_ARDUINO_STRING
-#define ARDUINOJSON_USE_ARDUINO_STRING 0
+#ifndef ARDUINOJSON_ENABLE_STD_STRING
+#define ARDUINOJSON_ENABLE_STD_STRING 1
+#endif
+
+// on a computer, there is no reason to beleive Arduino String is available
+#ifndef ARDUINOJSON_ENABLE_ARDUINO_STRING
+#define ARDUINOJSON_ENABLE_ARDUINO_STRING 0
 #endif
 
 // on a computer, we can assume that the STL is there

+ 5 - 5
include/ArduinoJson/Internals/DynamicStringBuilder.hpp

@@ -8,26 +8,26 @@
 #pragma once
 
 #include "../Print.hpp"
-#include "../String.hpp"
+#include "StringFuncs.hpp"
 
 namespace ArduinoJson {
 namespace Internals {
 
 // A Print implementation that allows to write in a String
+template <typename TString>
 class DynamicStringBuilder : public Print {
  public:
-  DynamicStringBuilder(String &str) : _str(str) {}
+  DynamicStringBuilder(TString &str) : _str(str) {}
 
   virtual size_t write(uint8_t c) {
-    // Need to cast to char, otherwise String will print a number (issue #120)
-    _str += static_cast<char>(c);
+    StringFuncs<TString>::append(_str, static_cast<char>(c));
     return 1;
   }
 
  private:
   DynamicStringBuilder &operator=(const DynamicStringBuilder &);
 
-  String &_str;
+  TString &_str;
 };
 }
 }

+ 9 - 4
include/ArduinoJson/Internals/JsonPrintable.hpp

@@ -8,6 +8,7 @@
 #pragma once
 
 #include "../Configuration.hpp"
+#include "../TypeTraits/EnableIf.hpp"
 #include "DummyPrint.hpp"
 #include "DynamicStringBuilder.hpp"
 #include "IndentedPrint.hpp"
@@ -49,8 +50,10 @@ class JsonPrintable {
     return printTo(sb);
   }
 
-  size_t printTo(String &str) const {
-    DynamicStringBuilder sb(str);
+  template <typename TString>
+  typename TypeTraits::EnableIf<StringFuncs<TString>::has_append, size_t>::type
+  printTo(TString &str) const {
+    DynamicStringBuilder<TString> sb(str);
     return printTo(sb);
   }
 
@@ -69,8 +72,10 @@ class JsonPrintable {
     return prettyPrintTo(indentedPrint);
   }
 
-  size_t prettyPrintTo(String &str) const {
-    DynamicStringBuilder sb(str);
+  template <typename TString>
+  typename TypeTraits::EnableIf<StringFuncs<TString>::has_append, size_t>::type
+  prettyPrintTo(TString &str) const {
+    DynamicStringBuilder<TString> sb(str);
     return prettyPrintTo(sb);
   }
 

+ 9 - 7
include/ArduinoJson/TypeTraits/IsReference.hpp → include/ArduinoJson/Internals/JsonVariantDefault.hpp

@@ -8,17 +8,19 @@
 #pragma once
 
 namespace ArduinoJson {
-namespace TypeTraits {
+namespace Internals {
 
-// A meta-function that returns true if T is a reference
 template <typename T>
-struct IsReference {
-  static const bool value = false;
+struct JsonVariantDefault {
+  static T get() {
+    return T();
+  }
 };
 
 template <typename T>
-struct IsReference<T&> {
-  static const bool value = true;
-};
+struct JsonVariantDefault<const T> : JsonVariantDefault<T> {};
+
+template <typename T>
+struct JsonVariantDefault<T&> : JsonVariantDefault<T> {};
 }
 }

+ 94 - 0
include/ArduinoJson/Internals/StringFuncs.hpp

@@ -0,0 +1,94 @@
+// Copyright Benoit Blanchon 2014-2016
+// MIT License
+//
+// Arduino JSON library
+// https://github.com/bblanchon/ArduinoJson
+// If you like this project, please add a star!
+
+#pragma once
+
+#include "../Configuration.hpp"
+
+#if ARDUINOJSON_ENABLE_ARDUINO_STRING
+#include <WString.h>
+#endif
+
+#if ARDUINOJSON_ENABLE_STD_STRING
+#include <string>
+#endif
+
+namespace ArduinoJson {
+namespace Internals {
+
+template <typename TString>
+struct StringFuncs {};
+
+template <typename TString>
+struct StringFuncs<const TString> : StringFuncs<TString> {};
+
+template <typename TString>
+struct StringFuncs<TString&> : StringFuncs<TString> {};
+
+struct CharPtrFuncs {
+  static bool equals(const char* str, const char* expected) {
+    return strcmp(str, expected) == 0;
+  }
+
+  template <typename Buffer>
+  static char* duplicate(const char* str, Buffer* buffer) {
+    if (!str) return NULL;
+    size_t size = strlen(str) + 1;
+    void* dup = buffer->alloc(size);
+    if (dup != NULL) memcpy(dup, str, size);
+    return static_cast<char*>(dup);
+  }
+
+  static const bool has_append = false;
+  static const bool should_duplicate = false;
+};
+
+template <>
+struct StringFuncs<const char*> : CharPtrFuncs {};
+
+template <>
+struct StringFuncs<char*> : CharPtrFuncs {};
+
+template <size_t N>
+struct StringFuncs<char[N]> : CharPtrFuncs {};
+
+template <typename TString>
+struct StdStringFuncs {
+  template <typename Buffer>
+  static char* duplicate(const TString& str, Buffer* buffer) {
+    if (!str.c_str()) return NULL;  // <- Arduino string can return NULL
+    size_t size = str.length() + 1;
+    void* dup = buffer->alloc(size);
+    if (dup != NULL) memcpy(dup, str.c_str(), size);
+    return static_cast<char*>(dup);
+  }
+
+  static bool equals(const TString& str, const char* expected) {
+    return str == expected;
+  }
+
+  static void append(TString& str, char c) {
+    str += c;
+  }
+
+  static const bool has_append = true;
+  static const bool should_duplicate = true;
+};
+
+#if ARDUINOJSON_ENABLE_ARDUINO_STRING
+template <>
+struct StringFuncs<String> : StdStringFuncs<String> {};
+template <>
+struct StringFuncs<StringSumHelper> : StdStringFuncs<StringSumHelper> {};
+#endif
+
+#if ARDUINOJSON_ENABLE_STD_STRING
+template <>
+struct StringFuncs<std::string> : StdStringFuncs<std::string> {};
+#endif
+}
+}

+ 41 - 0
include/ArduinoJson/Internals/ValueSetter.hpp

@@ -0,0 +1,41 @@
+// Copyright Benoit Blanchon 2014-2016
+// MIT License
+//
+// Arduino JSON library
+// https://github.com/bblanchon/ArduinoJson
+// If you like this project, please add a star!
+
+#pragma once
+
+#include "../JsonBuffer.hpp"
+#include "../JsonVariant.hpp"
+#include "../TypeTraits/EnableIf.hpp"
+#include "StringFuncs.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+template <typename TSource, typename Enable = void>
+struct ValueSetter {
+  template <typename TDestination>
+  static bool set(JsonBuffer*, TDestination& destination,
+                  const TSource& source) {
+    destination = source;
+    return true;
+  }
+};
+
+template <typename TSource>
+struct ValueSetter<TSource, typename TypeTraits::EnableIf<
+                                StringFuncs<TSource>::should_duplicate>::type> {
+  template <typename TDestination>
+  static bool set(JsonBuffer* buffer, TDestination& destination,
+                  const TSource& source) {
+    const char* copy = buffer->strdup(source);
+    if (!copy) return false;
+    destination = copy;
+    return true;
+  }
+};
+}
+}

+ 40 - 61
include/ArduinoJson/JsonArray.hpp

@@ -11,10 +11,12 @@
 #include "Internals/JsonPrintable.hpp"
 #include "Internals/List.hpp"
 #include "Internals/ReferenceType.hpp"
+#include "Internals/StringFuncs.hpp"
+#include "Internals/ValueSetter.hpp"
 #include "JsonVariant.hpp"
+#include "TypeTraits/ConstRefOrConstPtr.hpp"
 #include "TypeTraits/EnableIf.hpp"
 #include "TypeTraits/IsFloatingPoint.hpp"
-#include "TypeTraits/IsReference.hpp"
 #include "TypeTraits/IsSame.hpp"
 
 // Returns the size (in bytes) of an array with n elements.
@@ -40,15 +42,6 @@ class JsonArray : public Internals::JsonPrintable<JsonArray>,
                   public Internals::List<JsonVariant>,
                   public Internals::JsonBufferAllocated {
  public:
-  // A meta-function that returns true if type T can be used in
-  // JsonArray::set()
-  template <typename T>
-  struct CanSet {
-    static const bool value = JsonVariant::IsConstructibleFrom<T>::value ||
-                              TypeTraits::IsSame<T, String &>::value ||
-                              TypeTraits::IsSame<T, const String &>::value;
-  };
-
   // Create an empty JsonArray attached to the specified JsonBuffer.
   // You should not call this constructor directly.
   // Instead, use JsonBuffer::createArray() or JsonBuffer::parseArray().
@@ -57,7 +50,7 @@ class JsonArray : public Internals::JsonPrintable<JsonArray>,
 
   // Gets the value at the specified index
   JsonVariant operator[](size_t index) const {
-    return get(index);
+    return get<JsonVariant>(index);
   }
 
   // Gets or sets the value at specified index
@@ -73,29 +66,24 @@ class JsonArray : public Internals::JsonPrintable<JsonArray>,
   // bool add(float value);
   // bool add(double value);
   // bool add(const char*);
-  template <typename T>
-  bool add(
-      T value,
-      typename TypeTraits::EnableIf<
-          CanSet<T>::value && !TypeTraits::IsReference<T>::value>::type * = 0) {
-    return addNode<T>(value);
-  }
+  // bool add(const char[]);
+  // bool add(const char[N]);
+  // bool add(RawJson);
+  // bool add(const std::string&)
   // bool add(const String&)
   // bool add(const JsonVariant&);
   // bool add(JsonArray&);
   // bool add(JsonObject&);
   template <typename T>
-  bool add(const T &value,
-           typename TypeTraits::EnableIf<CanSet<T &>::value>::type * = 0) {
-    return addNode<T &>(const_cast<T &>(value));
+  bool add(const T &value) {
+    // reduce the number of template function instanciation to reduce code size
+    return addNodeImpl<typename TypeTraits::ConstRefOrConstPtr<T>::type>(value);
   }
   // bool add(float value, uint8_t decimals);
   // bool add(double value, uint8_t decimals);
   template <typename T>
-  bool add(T value, uint8_t decimals,
-           typename TypeTraits::EnableIf<
-               TypeTraits::IsFloatingPoint<T>::value>::type * = 0) {
-    return addNode<JsonVariant>(JsonVariant(value, decimals));
+  bool add(T value, uint8_t decimals) {
+    return add(JsonVariant(value, decimals));
   }
 
   // Sets the value at specified index.
@@ -104,42 +92,33 @@ class JsonArray : public Internals::JsonPrintable<JsonArray>,
   // bool set(size_t index, long value);
   // bool set(size_t index, int value);
   // bool set(size_t index, short value);
-  template <typename T>
-  bool set(
-      size_t index, T value,
-      typename TypeTraits::EnableIf<
-          CanSet<T>::value && !TypeTraits::IsReference<T>::value>::type * = 0) {
-    return setNodeAt<T>(index, value);
-  }
+  // bool set(size_t index, const std::string&)
   // bool set(size_t index, const String&)
   // bool set(size_t index, const JsonVariant&);
   // bool set(size_t index, JsonArray&);
   // bool set(size_t index, JsonObject&);
   template <typename T>
-  bool set(size_t index, const T &value,
-           typename TypeTraits::EnableIf<CanSet<T &>::value>::type * = 0) {
-    return setNodeAt<T &>(index, const_cast<T &>(value));
+  bool set(size_t index, const T &value) {
+    // reduce the number of template function instanciation to reduce code size
+    return setNodeAt<typename TypeTraits::ConstRefOrConstPtr<T>::type>(index,
+                                                                       value);
   }
   // bool set(size_t index, float value, uint8_t decimals = 2);
   // bool set(size_t index, double value, uint8_t decimals = 2);
   template <typename T>
-  bool set(size_t index, T value, uint8_t decimals,
-           typename TypeTraits::EnableIf<
-               TypeTraits::IsFloatingPoint<T>::value>::type * = 0) {
-    return setNodeAt<const JsonVariant &>(index, JsonVariant(value, decimals));
-  }
-
-  // Gets the value at the specified index.
-  JsonVariant get(size_t index) const {
-    node_type *node = getNodeAt(index);
-    return node ? node->content : JsonVariant();
+  typename TypeTraits::EnableIf<TypeTraits::IsFloatingPoint<T>::value,
+                                bool>::type
+  set(size_t index, T value, uint8_t decimals) {
+    return set(index, JsonVariant(value, decimals));
   }
 
   // Gets the value at the specified index.
   template <typename T>
   typename Internals::JsonVariantAs<T>::type get(size_t index) const {
     node_type *node = getNodeAt(index);
-    return node ? node->content.as<T>() : JsonVariant::defaultValue<T>();
+    return node ? node->content.as<T>()
+                : Internals::JsonVariantDefault<T>::get();
+    ;
   }
 
   // Check the type of the value at specified index.
@@ -172,7 +151,7 @@ class JsonArray : public Internals::JsonPrintable<JsonArray>,
 
   // Imports a 1D array
   template <typename T, size_t N>
-  bool copyFrom(T(&array)[N]) {
+  bool copyFrom(T (&array)[N]) {
     return copyFrom(array, N);
   }
 
@@ -188,7 +167,7 @@ class JsonArray : public Internals::JsonPrintable<JsonArray>,
 
   // Imports a 2D array
   template <typename T, size_t N1, size_t N2>
-  bool copyFrom(T(&array)[N1][N2]) {
+  bool copyFrom(T (&array)[N1][N2]) {
     bool ok = true;
     for (size_t i = 0; i < N1; i++) {
       JsonArray &nestedArray = createNestedArray();
@@ -201,7 +180,7 @@ class JsonArray : public Internals::JsonPrintable<JsonArray>,
 
   // Exports a 1D array
   template <typename T, size_t N>
-  size_t copyTo(T(&array)[N]) const {
+  size_t copyTo(T (&array)[N]) const {
     return copyTo(array, N);
   }
 
@@ -216,7 +195,7 @@ class JsonArray : public Internals::JsonPrintable<JsonArray>,
 
   // Exports a 2D array
   template <typename T, size_t N1, size_t N2>
-  void copyTo(T(&array)[N1][N2]) const {
+  void copyTo(T (&array)[N1][N2]) const {
     size_t i = 0;
     for (const_iterator it = begin(); it != end() && i < N1; ++it) {
       it->asArray().copyTo(array[i++]);
@@ -230,22 +209,22 @@ class JsonArray : public Internals::JsonPrintable<JsonArray>,
     return node;
   }
 
-  template <typename TValue>
-  bool setNodeAt(size_t index, TValue value) {
+  template <typename TValueRef>
+  bool setNodeAt(size_t index, TValueRef value) {
     node_type *node = getNodeAt(index);
-    return node != NULL && setNodeValue<TValue>(node, value);
+    if (!node) return false;
+
+    return Internals::ValueSetter<TValueRef>::set(_buffer, node->content,
+                                                  value);
   }
 
-  template <typename TValue>
-  bool addNode(TValue value) {
+  template <typename TValueRef>
+  bool addNodeImpl(TValueRef value) {
     node_type *node = addNewNode();
-    return node != NULL && setNodeValue<TValue>(node, value);
-  }
+    if (!node) return false;
 
-  template <typename T>
-  bool setNodeValue(node_type *node, T value) {
-    node->content = value;
-    return true;
+    return Internals::ValueSetter<TValueRef>::set(_buffer, node->content,
+                                                  value);
   }
 };
 }

+ 13 - 31
include/ArduinoJson/JsonArray.ipp

@@ -13,50 +13,31 @@
 
 namespace ArduinoJson {
 
-inline JsonVariant::JsonVariant(JsonArray &array) {
+inline JsonVariant::JsonVariant(const JsonArray &array) {
   if (array.success()) {
     _type = Internals::JSON_ARRAY;
-    _content.asArray = &array;
+    _content.asArray = const_cast<JsonArray *>(&array);
   } else {
     _type = Internals::JSON_UNDEFINED;
   }
 }
 
-inline JsonVariant::JsonVariant(JsonObject &object) {
+inline JsonVariant::JsonVariant(const JsonObject &object) {
   if (object.success()) {
     _type = Internals::JSON_OBJECT;
-    _content.asObject = &object;
+    _content.asObject = const_cast<JsonObject *>(&object);
   } else {
     _type = Internals::JSON_UNDEFINED;
   }
 }
 
+namespace Internals {
 template <>
-inline bool JsonArray::setNodeValue(node_type *node, String &value) {
-  const char *copy = _buffer->strdup(value);
-  if (!copy) return false;
-  node->content = copy;
-  return true;
-}
-
-template <>
-inline JsonArray &JsonVariant::defaultValue<JsonArray>() {
-  return JsonArray::invalid();
-}
-
-template <>
-inline JsonArray &JsonVariant::defaultValue<JsonArray &>() {
-  return JsonArray::invalid();
-}
-
-template <>
-inline const JsonArray &JsonVariant::defaultValue<const JsonArray>() {
-  return JsonArray::invalid();
-}
-
-template <>
-inline const JsonArray &JsonVariant::defaultValue<const JsonArray &>() {
-  return JsonArray::invalid();
+struct JsonVariantDefault<JsonArray> {
+  static JsonArray &get() {
+    return JsonArray::invalid();
+  }
+};
 }
 
 inline JsonArray &JsonVariant::asArray() const {
@@ -71,10 +52,11 @@ inline JsonArray &JsonArray::createNestedArray() {
   return array;
 }
 
-inline JsonArray &JsonObject::createNestedArray(JsonObjectKey key) {
+template <typename TString>
+inline JsonArray &JsonObject::createNestedArray(const TString &key) {
   if (!_buffer) return JsonArray::invalid();
   JsonArray &array = _buffer->createArray();
-  setNodeAt<const JsonVariant &>(key, array);
+  set(key, array);
   return array;
 }
 }

+ 5 - 19
include/ArduinoJson/JsonArraySubscript.hpp

@@ -21,24 +21,14 @@ class JsonArraySubscript : public JsonVariantBase<JsonArraySubscript> {
   FORCE_INLINE JsonArraySubscript(JsonArray& array, size_t index)
       : _array(array), _index(index) {}
 
-  JsonArraySubscript& operator=(const JsonArraySubscript& src) {
-    _array.set<const JsonVariant&>(_index, src);
+  FORCE_INLINE JsonArraySubscript& operator=(const JsonArraySubscript& src) {
+    _array.set(_index, src);
     return *this;
   }
 
   template <typename T>
-  typename TypeTraits::EnableIf<JsonArray::CanSet<T&>::value,
-                                JsonArraySubscript>::type&
-  operator=(const T& src) {
-    _array.set<T&>(_index, const_cast<T&>(src));
-    return *this;
-  }
-
-  template <typename T>
-  typename TypeTraits::EnableIf<JsonArray::CanSet<T>::value,
-                                JsonArraySubscript>::type&
-  operator=(T src) {
-    _array.set<T>(_index, src);
+  FORCE_INLINE JsonArraySubscript& operator=(const T& src) {
+    _array.set(_index, src);
     return *this;
   }
 
@@ -46,10 +36,6 @@ class JsonArraySubscript : public JsonVariantBase<JsonArraySubscript> {
     return _index < _array.size();
   }
 
-  FORCE_INLINE operator JsonVariant() const {
-    return _array.get(_index);
-  }
-
   template <typename T>
   FORCE_INLINE typename Internals::JsonVariantAs<T>::type as() const {
     return _array.get<T>(_index);
@@ -61,7 +47,7 @@ class JsonArraySubscript : public JsonVariantBase<JsonArraySubscript> {
   }
 
   template <typename TValue>
-  void set(TValue value) {
+  FORCE_INLINE void set(const TValue& value) {
     _array.set(_index, value);
   }
 

+ 20 - 49
include/ArduinoJson/JsonBuffer.hpp

@@ -12,7 +12,6 @@
 #include <string.h>
 
 #include "JsonVariant.hpp"
-#include "String.hpp"
 
 #if defined(__clang__)
 #pragma clang diagnostic push
@@ -58,67 +57,56 @@ class JsonBuffer {
   // writable
   // because the parser will insert null-terminators and replace escaped chars.
   //
-  // The second argument set the nesting limit (see comment on DEFAULT_LIMIT)
+  // The second argument set the nesting limit
   //
   // Returns a reference to the new JsonObject or JsonObject::invalid() if the
   // allocation fails.
-  JsonArray &parseArray(char *json, uint8_t nestingLimit = DEFAULT_LIMIT);
+  JsonArray &parseArray(
+      char *json, uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT);
 
-  // Same with a const char*.
   // With this overload, the JsonBuffer will make a copy of the string
-  JsonArray &parseArray(const char *json, uint8_t nesting = DEFAULT_LIMIT) {
+  template <typename TString>
+  JsonArray &parseArray(const TString &json,
+                        uint8_t nesting = ARDUINOJSON_DEFAULT_NESTING_LIMIT) {
     return parseArray(strdup(json), nesting);
   }
 
-  // Same as above with a String class
-  JsonArray &parseArray(const String &json, uint8_t nesting = DEFAULT_LIMIT) {
-    return parseArray(json.c_str(), nesting);
-  }
-
   // Allocates and populate a JsonObject from a JSON string.
   //
   // The First argument is a pointer to the JSON string, the memory must be
   // writable
   // because the parser will insert null-terminators and replace escaped chars.
   //
-  // The second argument set the nesting limit (see comment on DEFAULT_LIMIT)
+  // The second argument set the nesting limit
   //
   // Returns a reference to the new JsonObject or JsonObject::invalid() if the
   // allocation fails.
-  JsonObject &parseObject(char *json, uint8_t nestingLimit = DEFAULT_LIMIT);
+  JsonObject &parseObject(
+      char *json, uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT);
 
-  // Same with a const char*.
   // With this overload, the JsonBuffer will make a copy of the string
-  JsonObject &parseObject(const char *json, uint8_t nesting = DEFAULT_LIMIT) {
+  template <typename TString>
+  JsonObject &parseObject(const TString &json,
+                          uint8_t nesting = ARDUINOJSON_DEFAULT_NESTING_LIMIT) {
     return parseObject(strdup(json), nesting);
   }
 
-  // Same as above with a String class
-  JsonObject &parseObject(const String &json, uint8_t nesting = DEFAULT_LIMIT) {
-    return parseObject(json.c_str(), nesting);
-  }
-
   // Generalized version of parseArray() and parseObject(), also works for
   // integral types.
-  JsonVariant parse(char *json, uint8_t nestingLimit = DEFAULT_LIMIT);
+  JsonVariant parse(char *json,
+                    uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT);
 
-  // Same with a const char*.
   // With this overload, the JsonBuffer will make a copy of the string
-  JsonVariant parse(const char *json, uint8_t nesting = DEFAULT_LIMIT) {
+  template <typename TString>
+  JsonVariant parse(const TString &json,
+                    uint8_t nesting = ARDUINOJSON_DEFAULT_NESTING_LIMIT) {
     return parse(strdup(json), nesting);
   }
 
-  // Same as above with a String class
-  JsonVariant parse(const String &json, uint8_t nesting = DEFAULT_LIMIT) {
-    return parse(json.c_str(), nesting);
-  }
-
   // Duplicate a string
-  char *strdup(const char *src) {
-    return src ? strdup(src, strlen(src)) : NULL;
-  }
-  char *strdup(const String &src) {
-    return strdup(src.c_str(), src.length());
+  template <typename TString>
+  char *strdup(const TString &src) {
+    return Internals::StringFuncs<TString>::duplicate(src, this);
   }
 
   // Allocates n bytes in the JsonBuffer.
@@ -135,23 +123,6 @@ class JsonBuffer {
     return bytes;
 #endif
   }
-
- private:
-  char *strdup(const char *, size_t);
-
-  // Default value of nesting limit of parseArray() and parseObject().
-  //
-  // The nesting limit is a constrain on the level of nesting allowed in the
-  // JSON string.
-  // If set to 0, only a flat array or objects can be parsed.
-  // If set to 1, the object can contain nested arrays or objects but only 1
-  // level deep.
-  // And bigger values will allow more level of nesting.
-  //
-  // The purpose of this feature is to prevent stack overflow that could
-  // lead to
-  // a security risk.
-  static const uint8_t DEFAULT_LIMIT = ARDUINOJSON_DEFAULT_NESTING_LIMIT;
 };
 }
 

+ 0 - 8
include/ArduinoJson/JsonBuffer.ipp

@@ -36,11 +36,3 @@ inline ArduinoJson::JsonVariant ArduinoJson::JsonBuffer::parse(
   Internals::JsonParser parser(this, json, nestingLimit);
   return parser.parseVariant();
 }
-
-inline char *ArduinoJson::JsonBuffer::strdup(const char *source,
-                                             size_t length) {
-  size_t size = length + 1;
-  char *dest = static_cast<char *>(alloc(size));
-  if (dest != NULL) memcpy(dest, source, size);
-  return dest;
-}

+ 59 - 73
include/ArduinoJson/JsonObject.hpp

@@ -7,15 +7,16 @@
 
 #pragma once
 
-#include "String.hpp"
 #include "Internals/JsonBufferAllocated.hpp"
 #include "Internals/JsonPrintable.hpp"
 #include "Internals/List.hpp"
 #include "Internals/ReferenceType.hpp"
+#include "Internals/StringFuncs.hpp"
+#include "Internals/ValueSetter.hpp"
 #include "JsonPair.hpp"
+#include "TypeTraits/ConstRefOrConstPtr.hpp"
 #include "TypeTraits/EnableIf.hpp"
 #include "TypeTraits/IsFloatingPoint.hpp"
-#include "TypeTraits/IsReference.hpp"
 #include "TypeTraits/IsSame.hpp"
 
 // Returns the size (in bytes) of an object with n elements.
@@ -40,27 +41,19 @@ class JsonObject : public Internals::JsonPrintable<JsonObject>,
                    public Internals::List<JsonPair>,
                    public Internals::JsonBufferAllocated {
  public:
-  // A meta-function that returns true if type T can be used in
-  // JsonObject::set()
-  template <typename T>
-  struct CanSet {
-    static const bool value = JsonVariant::IsConstructibleFrom<T>::value ||
-                              TypeTraits::IsSame<T, String&>::value ||
-                              TypeTraits::IsSame<T, const String&>::value;
-  };
-
   // Create an empty JsonArray attached to the specified JsonBuffer.
   // You should not use this constructor directly.
   // Instead, use JsonBuffer::createObject() or JsonBuffer.parseObject().
   explicit JsonObject(JsonBuffer* buffer) : Internals::List<JsonPair>(buffer) {}
 
   // Gets or sets the value associated with the specified key.
-  JsonObjectSubscript<const char*> operator[](const char* key);
-  JsonObjectSubscript<const String&> operator[](const String& key);
+  template <typename TString>
+  JsonObjectSubscript<TString> operator[](const TString& key);
 
   // Gets the value associated with the specified key.
-  JsonVariant operator[](JsonObjectKey key) const {
-    return get(key);
+  template <typename TString>
+  JsonVariant operator[](const TString& key) const {
+    return get<JsonVariant>(key);
   }
 
   // Sets the specified key with the specified value.
@@ -73,67 +66,62 @@ class JsonObject : public Internals::JsonPrintable<JsonObject>,
   // bool set(TKey key, double value);
   // bool set(TKey key, const char* value);
   // bool set(TKey key, RawJson value);
-  template <typename T>
-  bool set(
-      JsonObjectKey key, T value,
-      typename TypeTraits::EnableIf<
-          CanSet<T>::value && !TypeTraits::IsReference<T>::value>::type* = 0) {
-    return setNodeAt<T>(key, value);
-  }
   // bool set(Key, String&);
   // bool set(Key, JsonArray&);
   // bool set(Key, JsonObject&);
   // bool set(Key, JsonVariant&);
-  template <typename T>
-  bool set(JsonObjectKey key, const T& value,
-           typename TypeTraits::EnableIf<CanSet<T&>::value>::type* = 0) {
-    return setNodeAt<T&>(key, const_cast<T&>(value));
+  template <typename TValue, typename TString>
+  bool set(const TString& key, const TValue& value) {
+    // reduce the number of template function instanciation to reduce code size
+    return setNodeAt<typename TypeTraits::ConstRefOrConstPtr<TString>::type,
+                     typename TypeTraits::ConstRefOrConstPtr<TValue>::type>(
+        key, value);
   }
   // bool set(Key, float value, uint8_t decimals);
   // bool set(Key, double value, uint8_t decimals);
-  template <typename TValue>
-  bool set(JsonObjectKey key, TValue value, uint8_t decimals,
-           typename TypeTraits::EnableIf<
-               TypeTraits::IsFloatingPoint<TValue>::value>::type* = 0) {
-    return setNodeAt<const JsonVariant&>(key, JsonVariant(value, decimals));
-  }
-
-  // Gets the value associated with the specified key.
-  JsonVariant get(JsonObjectKey key) const {
-    node_type* node = getNodeAt(key.c_str());
-    return node ? node->content.value : JsonVariant();
+  template <typename TValue, typename TString>
+  typename TypeTraits::EnableIf<TypeTraits::IsFloatingPoint<TValue>::value,
+                                bool>::type
+  set(const TString& key, TValue value, uint8_t decimals) {
+    return set(key, JsonVariant(value, decimals));
   }
 
   // Gets the value associated with the specified key.
-  template <typename T>
-  typename Internals::JsonVariantAs<T>::type get(JsonObjectKey key) const {
-    node_type* node = getNodeAt(key.c_str());
-    return node ? node->content.value.as<T>() : JsonVariant::defaultValue<T>();
+  template <typename TValue, typename TString>
+  typename Internals::JsonVariantAs<TValue>::type get(
+      const TString& key) const {
+    node_type* node = getNodeAt(key);
+    return node ? node->content.value.as<TValue>()
+                : Internals::JsonVariantDefault<TValue>::get();
   }
 
   // Checks the type of the value associated with the specified key.
-  template <typename T>
-  bool is(JsonObjectKey key) const {
-    node_type* node = getNodeAt(key.c_str());
-    return node ? node->content.value.is<T>() : false;
+  template <typename TValue, typename TString>
+  bool is(const TString& key) const {
+    node_type* node = getNodeAt(key);
+    return node ? node->content.value.is<TValue>() : false;
   }
 
   // Creates and adds a JsonArray.
   // This is a shortcut for JsonBuffer::createArray() and JsonObject::add().
-  JsonArray& createNestedArray(JsonObjectKey key);
+  template <typename TString>
+  JsonArray& createNestedArray(const TString& key);
 
   // Creates and adds a JsonObject.
   // This is a shortcut for JsonBuffer::createObject() and JsonObject::add().
-  JsonObject& createNestedObject(JsonObjectKey key);
+  template <typename TString>
+  JsonObject& createNestedObject(const TString& key);
 
   // Tells weither the specified key is present and associated with a value.
-  bool containsKey(JsonObjectKey key) const {
-    return getNodeAt(key.c_str()) != NULL;
+  template <typename TString>
+  bool containsKey(const TString& key) const {
+    return getNodeAt(key) != NULL;
   }
 
   // Removes the specified key and the associated value.
-  void remove(JsonObjectKey key) {
-    removeNode(getNodeAt(key.c_str()));
+  template <typename TString>
+  void remove(const TString& key) {
+    removeNode(getNodeAt(key));
   }
 
   // Returns a reference an invalid JsonObject.
@@ -146,37 +134,35 @@ class JsonObject : public Internals::JsonPrintable<JsonObject>,
 
  private:
   // Returns the list node that matches the specified key.
-  node_type* getNodeAt(const char* key) const {
+  template <typename TString>
+  node_type* getNodeAt(const TString& key) const {
+    // reduce the number of template function instanciation to reduce code size
+    return getNodeAtImpl<
+        typename TypeTraits::ConstRefOrConstPtr<TString>::type>(key);
+  }
+
+  template <typename TStringRef>
+  node_type* getNodeAtImpl(TStringRef key) const {
     for (node_type* node = _firstNode; node; node = node->next) {
-      if (!strcmp(node->content.key, key)) return node;
+      if (Internals::StringFuncs<TStringRef>::equals(key, node->content.key))
+        return node;
     }
     return NULL;
   }
 
-  template <typename T>
-  bool setNodeAt(JsonObjectKey key, T value) {
-    node_type* node = getNodeAt(key.c_str());
+  template <typename TStringRef, typename TValueRef>
+  bool setNodeAt(TStringRef key, TValueRef value) {
+    node_type* node = getNodeAtImpl<TStringRef>(key);
     if (!node) {
       node = addNewNode();
-      if (!node || !setNodeKey(node, key)) return false;
-    }
-    return setNodeValue<T>(node, value);
-  }
+      if (!node) return false;
 
-  bool setNodeKey(node_type* node, JsonObjectKey key) {
-    if (key.needs_copy()) {
-      node->content.key = _buffer->strdup(key.c_str());
-      if (node->content.key == NULL) return false;
-    } else {
-      node->content.key = key.c_str();
+      bool key_ok = Internals::ValueSetter<TStringRef>::set(
+          _buffer, node->content.key, key);
+      if (!key_ok) return false;
     }
-    return true;
-  }
-
-  template <typename T>
-  bool setNodeValue(node_type* node, T value) {
-    node->content.value = value;
-    return true;
+    return Internals::ValueSetter<TValueRef>::set(_buffer, node->content.value,
+                                                  value);
   }
 };
 }

+ 11 - 35
include/ArduinoJson/JsonObject.ipp

@@ -13,38 +13,13 @@
 
 namespace ArduinoJson {
 
+namespace Internals {
 template <>
-inline bool JsonObject::setNodeValue(node_type *node, String &value) {
-  const char *dup = _buffer->strdup(value);
-  node->content.value = dup;
-  return dup != NULL;
-}
-
-template <>
-inline bool JsonObject::setNodeValue(node_type *node, const String &value) {
-  const char *dup = _buffer->strdup(value);
-  node->content.value = dup;
-  return dup != NULL;
-}
-
-template <>
-inline const JsonObject &JsonVariant::defaultValue<const JsonObject &>() {
-  return JsonObject::invalid();
-}
-
-template <>
-inline const JsonObject &JsonVariant::defaultValue<const JsonObject>() {
-  return JsonObject::invalid();
-}
-
-template <>
-inline JsonObject &JsonVariant::defaultValue<JsonObject &>() {
-  return JsonObject::invalid();
-}
-
-template <>
-inline JsonObject &JsonVariant::defaultValue<JsonObject>() {
-  return JsonObject::invalid();
+struct JsonVariantDefault<JsonObject> {
+  static JsonObject &get() {
+    return JsonObject::invalid();
+  }
+};
 }
 
 inline JsonObject &JsonVariant::asObject() const {
@@ -52,11 +27,12 @@ inline JsonObject &JsonVariant::asObject() const {
   return JsonObject::invalid();
 }
 
-inline JsonObject &JsonObject::createNestedObject(JsonObjectKey key) {
+template <typename TString>
+inline JsonObject &JsonObject::createNestedObject(const TString &key) {
   if (!_buffer) return JsonObject::invalid();
-  JsonObject &array = _buffer->createObject();
-  setNodeAt<const JsonVariant &>(key, array);
-  return array;
+  JsonObject &object = _buffer->createObject();
+  set(key, object);
+  return object;
 }
 
 inline JsonObject &JsonArray::createNestedObject() {

+ 0 - 27
include/ArduinoJson/JsonObjectKey.hpp

@@ -1,27 +0,0 @@
-// Copyright Benoit Blanchon 2014-2016
-// MIT License
-//
-// Arduino JSON library
-// https://github.com/bblanchon/ArduinoJson
-// If you like this project, please add a star!
-
-#pragma once
-
-#include "String.hpp"
-
-namespace ArduinoJson {
-
-// Represents a key in a JsonObject
-class JsonObjectKey {
- public:
-  JsonObjectKey(const char* key) : _value(key), _needs_copy(false) {}
-  JsonObjectKey(const String& key) : _value(key.c_str()), _needs_copy(true) {}
-
-  const char* c_str() const { return _value; }
-  bool needs_copy() const { return _needs_copy; }
-
- private:
-  const char* _value;
-  bool _needs_copy;
-};
-}

+ 29 - 54
include/ArduinoJson/JsonObjectSubscript.hpp

@@ -9,6 +9,7 @@
 
 #include "Configuration.hpp"
 #include "JsonVariantBase.hpp"
+#include "TypeTraits/ConstRefOrConstPtr.hpp"
 #include "TypeTraits/EnableIf.hpp"
 
 #ifdef _MSC_VER
@@ -18,30 +19,27 @@
 
 namespace ArduinoJson {
 
-template <typename TKey>
-class JsonObjectSubscript : public JsonVariantBase<JsonObjectSubscript<TKey> > {
+template <typename TString>
+class JsonObjectSubscript
+    : public JsonVariantBase<JsonObjectSubscript<TString> > {
+  // const String&
+  // const std::string&
+  // const char*
+  typedef typename TypeTraits::ConstRefOrConstPtr<TString>::type TStringRef;
+
  public:
-  FORCE_INLINE JsonObjectSubscript(JsonObject& object, TKey key)
+  FORCE_INLINE JsonObjectSubscript(JsonObject& object, TStringRef key)
       : _object(object), _key(key) {}
 
-  JsonObjectSubscript<TKey>& operator=(const JsonObjectSubscript<TKey>& src) {
-    _object.set<const JsonVariant&>(_key, src);
-    return *this;
-  }
-
-  template <typename T>
-  typename TypeTraits::EnableIf<JsonObject::CanSet<T&>::value,
-                                JsonObjectSubscript<TKey> >::type&
-  operator=(const T& src) {
-    _object.set<T&>(_key, const_cast<T&>(src));
+  FORCE_INLINE JsonObjectSubscript<TString>& operator=(
+      const JsonObjectSubscript<TString>& src) {
+    _object.set(_key, src);
     return *this;
   }
 
   template <typename T>
-  typename TypeTraits::EnableIf<JsonObject::CanSet<T>::value,
-                                JsonObjectSubscript<TKey> >::type&
-  operator=(T src) {
-    _object.set<T>(_key, src);
+  FORCE_INLINE JsonObjectSubscript<TString>& operator=(const T& src) {
+    _object.set(_key, src);
     return *this;
   }
 
@@ -49,13 +47,9 @@ class JsonObjectSubscript : public JsonVariantBase<JsonObjectSubscript<TKey> > {
     return _object.containsKey(_key);
   }
 
-  FORCE_INLINE operator JsonVariant() const {
-    return _object.get(_key);
-  }
-
   template <typename TValue>
   FORCE_INLINE typename Internals::JsonVariantAs<TValue>::type as() const {
-    return _object.get<TValue>(_key);
+    return _object.get<TValue, TStringRef>(_key);
   }
 
   template <typename TValue>
@@ -64,58 +58,39 @@ class JsonObjectSubscript : public JsonVariantBase<JsonObjectSubscript<TKey> > {
   }
 
   template <typename TValue>
-  FORCE_INLINE bool set(TValue value) {
-    return _object.set<TValue>(_key, value);
+  FORCE_INLINE bool set(const TValue& value) {
+    return _object.set(_key, value);
   }
 
   template <typename TValue>
-  FORCE_INLINE bool set(TValue value, uint8_t decimals) {
+  FORCE_INLINE bool set(const TValue& value, uint8_t decimals) {
     return _object.set(_key, value, decimals);
   }
 
-  FORCE_INLINE JsonVariant get() {
-    return _object.get(_key);
-  }
-
  private:
   JsonObject& _object;
-  TKey _key;
+  TStringRef _key;
 };
 
 #if ARDUINOJSON_ENABLE_STD_STREAM
-inline std::ostream& operator<<(
-    std::ostream& os, const JsonObjectSubscript<const String&>& source) {
-  return source.printTo(os);
-}
-
-inline std::ostream& operator<<(
-    std::ostream& os, const JsonObjectSubscript<const char*>& source) {
+template <typename TString>
+inline std::ostream& operator<<(std::ostream& os,
+                                const JsonObjectSubscript<TString>& source) {
   return source.printTo(os);
 }
 #endif
 
-inline JsonObjectSubscript<const char*> JsonObject::operator[](
-    const char* key) {
-  return JsonObjectSubscript<const char*>(*this, key);
-}
-
-inline JsonObjectSubscript<const String&> JsonObject::operator[](
-    const String& key) {
-  return JsonObjectSubscript<const String&>(*this, key);
+template <typename TString>
+inline JsonObjectSubscript<TString> JsonObject::operator[](const TString& key) {
+  return JsonObjectSubscript<TString>(*this, key);
 }
 
 template <typename TImplem>
-inline const JsonObjectSubscript<const char*> JsonVariantBase<TImplem>::
-operator[](const char* key) const {
+template <class TString>
+inline const JsonObjectSubscript<TString> JsonVariantBase<TImplem>::operator[](
+    const TString& key) const {
   return asObject()[key];
 }
-
-template <typename TImplem>
-inline const JsonObjectSubscript<const String&> JsonVariantBase<TImplem>::
-operator[](const String& key) const {
-  return asObject()[key];
-}
-
 }  // namespace ArduinoJson
 
 #ifdef _MSC_VER

+ 0 - 1
include/ArduinoJson/JsonPair.hpp

@@ -7,7 +7,6 @@
 
 #pragma once
 
-#include "JsonObjectKey.hpp"
 #include "JsonVariant.hpp"
 
 namespace ArduinoJson {

+ 23 - 44
include/ArduinoJson/JsonVariant.hpp

@@ -12,6 +12,7 @@
 
 #include "Internals/JsonPrintable.hpp"
 #include "Internals/JsonVariantContent.hpp"
+#include "Internals/JsonVariantDefault.hpp"
 #include "Internals/JsonVariantType.hpp"
 #include "JsonVariantBase.hpp"
 #include "RawJson.hpp"
@@ -19,6 +20,8 @@
 #include "TypeTraits/IsFloatingPoint.hpp"
 #include "TypeTraits/IsIntegral.hpp"
 #include "TypeTraits/IsSame.hpp"
+#include "TypeTraits/IsSignedIntegral.hpp"
+#include "TypeTraits/IsUnsignedIntegral.hpp"
 #include "TypeTraits/RemoveConst.hpp"
 #include "TypeTraits/RemoveReference.hpp"
 
@@ -40,9 +43,6 @@ class JsonVariant : public JsonVariantBase<JsonVariant> {
                                                    JsonWriter &);
 
  public:
-  template <typename T>
-  struct IsConstructibleFrom;
-
   // Creates an uninitialized JsonVariant
   JsonVariant() : _type(Internals::JSON_UNDEFINED) {}
 
@@ -110,10 +110,14 @@ class JsonVariant : public JsonVariantBase<JsonVariant> {
   }
 
   // Create a JsonVariant containing a reference to an array.
-  JsonVariant(JsonArray &array);
+  // CAUTION: we are lying about constness, because the array can be modified if
+  // the variant is converted back to a JsonArray&
+  JsonVariant(const JsonArray &array);
 
   // Create a JsonVariant containing a reference to an object.
-  JsonVariant(JsonObject &object);
+  // CAUTION: we are lying about constness, because the object can be modified
+  // if the variant is converted back to a JsonObject&
+  JsonVariant(const JsonObject &object);
 
   // Get the variant as the specified type.
   //
@@ -146,14 +150,6 @@ class JsonVariant : public JsonVariantBase<JsonVariant> {
     return static_cast<T>(asFloat());
   }
   //
-  // const String as<String>() const;
-  template <typename T>
-  const typename TypeTraits::EnableIf<TypeTraits::IsSame<T, String>::value,
-                                      T>::type
-  as() const {
-    return toString();
-  }
-  //
   // const char* as<const char*>() const;
   // const char* as<char*>() const;
   template <typename T>
@@ -164,6 +160,18 @@ class JsonVariant : public JsonVariantBase<JsonVariant> {
     return asString();
   }
   //
+  // std::string as<std::string>() const;
+  // String as<String>() const;
+  template <typename T>
+  typename TypeTraits::EnableIf<Internals::StringFuncs<T>::has_append, T>::type
+  as() const {
+    const char *cstr = asString();
+    if (cstr) return T(cstr);
+    T s;
+    printTo(s);
+    return s;
+  }
+  //
   // const bool as<bool>() const
   template <typename T>
   const typename TypeTraits::EnableIf<TypeTraits::IsSame<T, bool>::value,
@@ -230,7 +238,8 @@ class JsonVariant : public JsonVariantBase<JsonVariant> {
   // int as<int>() const;
   // long as<long>() const;
   template <typename T>
-  const typename TypeTraits::EnableIf<TypeTraits::IsIntegral<T>::value,
+  const typename TypeTraits::EnableIf<TypeTraits::IsIntegral<T>::value &&
+                                          !TypeTraits::IsSame<T, bool>::value,
                                       bool>::type
   is() const {
     return isInteger();
@@ -296,12 +305,6 @@ class JsonVariant : public JsonVariantBase<JsonVariant> {
     return _type != Internals::JSON_UNDEFINED;
   }
 
-  // Value returned if the variant has an incompatible type
-  template <typename T>
-  static typename Internals::JsonVariantAs<T>::type defaultValue() {
-    return T();
-  }
-
   // DEPRECATED: use as<char*>() instead
   const char *asString() const;
 
@@ -317,7 +320,6 @@ class JsonVariant : public JsonVariantBase<JsonVariant> {
   JsonVariant(T value, typename TypeTraits::EnableIf<
                            TypeTraits::IsSame<T, char>::value>::type * = 0);
 
-  String toString() const;
   Internals::JsonFloat asFloat() const;
   Internals::JsonInteger asInteger() const;
   Internals::JsonUInt asUnsignedInteger() const;
@@ -350,27 +352,4 @@ inline JsonVariant float_with_n_digits(float value, uint8_t digits) {
 inline JsonVariant double_with_n_digits(double value, uint8_t digits) {
   return JsonVariant(value, digits);
 }
-
-template <typename T>
-struct JsonVariant::IsConstructibleFrom {
-  static const bool value =
-      TypeTraits::IsIntegral<T>::value ||
-      TypeTraits::IsFloatingPoint<T>::value ||
-      TypeTraits::IsSame<T, bool>::value ||
-      TypeTraits::IsSame<T, char *>::value ||
-      TypeTraits::IsSame<T, const char *>::value ||
-      TypeTraits::IsSame<T, RawJson>::value ||
-      TypeTraits::IsSame<T, JsonArray &>::value ||
-      TypeTraits::IsSame<T, const JsonArray &>::value ||
-      TypeTraits::IsSame<T, JsonArraySubscript &>::value ||
-      TypeTraits::IsSame<T, const JsonArraySubscript &>::value ||
-      TypeTraits::IsSame<T, JsonObject &>::value ||
-      TypeTraits::IsSame<T, const JsonObject &>::value ||
-      TypeTraits::IsSame<T, JsonObjectSubscript<const char *> &>::value ||
-      TypeTraits::IsSame<T, const JsonObjectSubscript<const char *> &>::value ||
-      TypeTraits::IsSame<T, JsonObjectSubscript<String> &>::value ||
-      TypeTraits::IsSame<T, const JsonObjectSubscript<String> &>::value ||
-      TypeTraits::IsSame<T, JsonVariant &>::value ||
-      TypeTraits::IsSame<T, const JsonVariant &>::value;
-};
 }

+ 2 - 13
include/ArduinoJson/JsonVariant.ipp

@@ -8,14 +8,14 @@
 #pragma once
 
 #include "Configuration.hpp"
-#include "JsonVariant.hpp"
 #include "Internals/Parse.hpp"
 #include "JsonArray.hpp"
 #include "JsonObject.hpp"
+#include "JsonVariant.hpp"
 
-#include <string.h>  // for strcmp
 #include <errno.h>   // for errno
 #include <stdlib.h>  // for strtol, strtod
+#include <string.h>  // for strcmp
 
 namespace ArduinoJson {
 
@@ -85,17 +85,6 @@ inline Internals::JsonFloat JsonVariant::asFloat() const {
   }
 }
 
-inline String JsonVariant::toString() const {
-  using namespace Internals;
-  String s;
-  if ((_type == JSON_STRING || _type == JSON_UNPARSED) &&
-      _content.asString != NULL)
-    s = _content.asString;
-  else
-    printTo(s);
-  return s;
-}
-
 inline bool JsonVariant::isBoolean() const {
   using namespace Internals;
   if (_type == JSON_BOOLEAN) return true;

+ 3 - 5
include/ArduinoJson/JsonVariantBase.hpp

@@ -8,7 +8,6 @@
 #pragma once
 
 #include "Internals/JsonVariantAs.hpp"
-#include "JsonObjectKey.hpp"
 #include "Polyfills/attributes.hpp"
 
 namespace ArduinoJson {
@@ -77,10 +76,9 @@ class JsonVariantBase : public Internals::JsonPrintable<TImpl> {
   // Returns the value associated with the specified key if the variant is
   // an object.
   // Return JsonVariant::invalid() if the variant is not an object.
-  FORCE_INLINE const JsonObjectSubscript<const char *> operator[](
-      const char *key) const;
-  FORCE_INLINE const JsonObjectSubscript<const String &> operator[](
-      const String &key) const;
+  template <typename TString>
+  FORCE_INLINE const JsonObjectSubscript<TString> operator[](
+      const TString &key) const;
 
  private:
   const TImpl *impl() const {

+ 3 - 1
include/ArduinoJson/RawJson.hpp

@@ -13,7 +13,9 @@ namespace ArduinoJson {
 class RawJson {
  public:
   explicit RawJson(const char* str) : _str(str) {}
-  operator const char*() const { return _str; }
+  operator const char*() const {
+    return _str;
+  }
 
  private:
   const char* _str;

+ 0 - 24
include/ArduinoJson/String.hpp

@@ -1,24 +0,0 @@
-// Copyright Benoit Blanchon 2014-2016
-// MIT License
-//
-// Arduino JSON library
-// https://github.com/bblanchon/ArduinoJson
-// If you like this project, please add a star!
-
-#pragma once
-
-#include "Configuration.hpp"
-
-#if ARDUINOJSON_USE_ARDUINO_STRING
-
-#include <WString.h>
-
-#else
-
-#include <string>
-
-namespace ArduinoJson {
-typedef std::string String;
-}
-
-#endif

+ 31 - 0
include/ArduinoJson/TypeTraits/ConstRefOrConstPtr.hpp

@@ -0,0 +1,31 @@
+// Copyright Benoit Blanchon 2014-2016
+// MIT License
+//
+// Arduino JSON library
+// https://github.com/bblanchon/ArduinoJson
+// If you like this project, please add a star!
+
+#pragma once
+
+namespace ArduinoJson {
+namespace TypeTraits {
+
+// A meta-function that return the type T without the const modifier
+template <typename T>
+struct ConstRefOrConstPtr {
+  typedef const T& type;
+};
+template <typename T>
+struct ConstRefOrConstPtr<T*> {
+  typedef const T* type;
+};
+template <typename T>
+struct ConstRefOrConstPtr<T[]> {
+  typedef const T* type;
+};
+template <typename T, size_t N>
+struct ConstRefOrConstPtr<T[N]> {
+  typedef const T* type;
+};
+}
+}

+ 5 - 2
include/ArduinoJson/TypeTraits/IsIntegral.hpp

@@ -7,7 +7,6 @@
 
 #pragma once
 
-#include "../Configuration.hpp"
 #include "IsSame.hpp"
 #include "IsSignedIntegral.hpp"
 #include "IsUnsignedIntegral.hpp"
@@ -20,7 +19,11 @@ template <typename T>
 struct IsIntegral {
   static const bool value = TypeTraits::IsSignedIntegral<T>::value ||
                             TypeTraits::IsUnsignedIntegral<T>::value ||
-                            TypeTraits::IsSame<T, char>::value;
+                            TypeTraits::IsSame<T, char>::value ||
+                            TypeTraits::IsSame<T, bool>::value;
 };
+
+template <typename T>
+struct IsIntegral<const T> : IsIntegral<T> {};
 }
 }

+ 14 - 1
test/DynamicJsonBuffer_Basic_Tests.cpp

@@ -5,8 +5,8 @@
 // https://github.com/bblanchon/ArduinoJson
 // If you like this project, please add a star!
 
-#include <gtest/gtest.h>
 #include <ArduinoJson.h>
+#include <gtest/gtest.h>
 
 class DynamicJsonBuffer_Basic_Tests : public testing::Test {
  protected:
@@ -38,3 +38,16 @@ TEST_F(DynamicJsonBuffer_Basic_Tests, Alignment) {
     ASSERT_EQ(0, addr & mask);
   }
 }
+
+TEST_F(DynamicJsonBuffer_Basic_Tests, strdup) {
+  char original[] = "hello";
+  char* copy = buffer.strdup(original);
+  strcpy(original, "world");
+  ASSERT_STREQ("hello", copy);
+}
+
+TEST_F(DynamicJsonBuffer_Basic_Tests, strdup_givenNull) {
+  const char* original = NULL;
+  char* copy = buffer.strdup(original);
+  ASSERT_EQ(NULL, copy);
+}

+ 26 - 0
test/JsonObject_Get_Tests.cpp

@@ -0,0 +1,26 @@
+// Copyright Benoit Blanchon 2014-2016
+// MIT License
+//
+// Arduino JSON library
+// https://github.com/bblanchon/ArduinoJson
+// If you like this project, please add a star!
+
+#include <ArduinoJson.h>
+#include <gtest/gtest.h>
+
+class JsonObject_Get_Tests : public ::testing::Test {
+ public:
+  JsonObject_Get_Tests() : _object(_jsonBuffer.createObject()) {}
+
+ protected:
+  DynamicJsonBuffer _jsonBuffer;
+  JsonObject& _object;
+};
+
+#define TEST_(name) TEST_F(JsonObject_Get_Tests, name)
+
+TEST_(GetConstCharPointer_GivenStringLiteral) {
+  _object.set("hello", "world");
+  const char* value = _object.get<const char*>("hello");
+  EXPECT_STREQ("world", value);
+}

+ 2 - 2
test/JsonObject_Invalid_Tests.cpp

@@ -5,11 +5,11 @@
 // https://github.com/bblanchon/ArduinoJson
 // If you like this project, please add a star!
 
-#include <gtest/gtest.h>
 #include <ArduinoJson.h>
+#include <gtest/gtest.h>
 
 TEST(JsonObject_Invalid_Tests, SubscriptFails) {
-  ASSERT_FALSE(JsonObject::invalid()[0].success());
+  ASSERT_FALSE(JsonObject::invalid()["key"].success());
 }
 
 TEST(JsonObject_Invalid_Tests, AddFails) {

+ 3 - 3
test/JsonObject_Set_Tests.cpp

@@ -5,8 +5,8 @@
 // https://github.com/bblanchon/ArduinoJson
 // If you like this project, please add a star!
 
-#include <gtest/gtest.h>
 #include <ArduinoJson.h>
+#include <gtest/gtest.h>
 
 class JsonObject_Set_Tests : public ::testing::Test {
  public:
@@ -112,7 +112,7 @@ TEST_(ShouldReturnTrue_WhenAllocationSucceeds) {
   StaticJsonBuffer<JSON_OBJECT_SIZE(1) + 15> jsonBuffer;
   JsonObject& obj = jsonBuffer.createObject();
 
-  bool result = obj.set(String("hello"), String("world"));
+  bool result = obj.set(std::string("hello"), std::string("world"));
 
   ASSERT_TRUE(result);
 }
@@ -121,7 +121,7 @@ TEST_(ShouldReturnFalse_WhenAllocationFails) {
   StaticJsonBuffer<JSON_OBJECT_SIZE(1) + 10> jsonBuffer;
   JsonObject& obj = jsonBuffer.createObject();
 
-  bool result = obj.set(String("hello"), String("world"));
+  bool result = obj.set(std::string("hello"), std::string("world"));
 
   ASSERT_FALSE(result);
 }

+ 8 - 8
test/JsonVariant_As_Tests.cpp

@@ -24,7 +24,7 @@ TEST(JsonVariant_As_Tests, DoubleAsCstr) {
 
 TEST(JsonVariant_As_Tests, DoubleAsString) {
   JsonVariant variant = 4.2;
-  ASSERT_EQ(String("4.20"), variant.as<String>());
+  ASSERT_EQ(std::string("4.20"), variant.as<std::string>());
 }
 
 TEST(JsonVariant_As_Tests, DoubleAsLong) {
@@ -64,7 +64,7 @@ TEST(JsonVariant_As_Tests, FalseAsLong) {
 
 TEST(JsonVariant_As_Tests, FalseAsString) {
   JsonVariant variant = false;
-  ASSERT_EQ(String("false"), variant.as<String>());
+  ASSERT_EQ(std::string("false"), variant.as<std::string>());
 }
 
 TEST(JsonVariant_As_Tests, TrueAsBool) {
@@ -84,7 +84,7 @@ TEST(JsonVariant_As_Tests, TrueAsLong) {
 
 TEST(JsonVariant_As_Tests, TrueAsString) {
   JsonVariant variant = true;
-  ASSERT_EQ(String("true"), variant.as<String>());
+  ASSERT_EQ(std::string("true"), variant.as<std::string>());
 }
 
 TEST(JsonVariant_As_Tests, LongAsBool) {
@@ -109,7 +109,7 @@ TEST(JsonVariant_As_Tests, NegativeLongAsDouble) {
 
 TEST(JsonVariant_As_Tests, LongAsString) {
   JsonVariant variant = 42L;
-  ASSERT_EQ(String("42"), variant.as<String>());
+  ASSERT_EQ(std::string("42"), variant.as<std::string>());
 }
 
 TEST(JsonVariant_As_Tests, LongZeroAsDouble) {
@@ -134,7 +134,7 @@ TEST(JsonVariant_As_Tests, NullAsLong) {
 
 TEST(JsonVariant_As_Tests, NullAsString) {
   JsonVariant variant = null;
-  ASSERT_EQ(String("null"), variant.as<String>());
+  ASSERT_EQ(std::string("null"), variant.as<std::string>());
 }
 
 TEST(JsonVariant_As_Tests, NumberStringAsBool) {
@@ -181,7 +181,7 @@ TEST(JsonVariant_As_Tests, RandomStringAsCharPtr) {
 
 TEST(JsonVariant_As_Tests, RandomStringAsString) {
   JsonVariant variant = "hello";
-  ASSERT_EQ(String("hello"), variant.as<String>());
+  ASSERT_EQ(std::string("hello"), variant.as<std::string>());
 }
 
 TEST(JsonVariant_As_Tests, TrueStringAsBool) {
@@ -201,7 +201,7 @@ TEST(JsonVariant_As_Tests, ObjectAsString) {
   obj["key"] = "value";
 
   JsonVariant variant = obj;
-  ASSERT_EQ(String("{\"key\":\"value\"}"), variant.as<String>());
+  ASSERT_EQ(std::string("{\"key\":\"value\"}"), variant.as<std::string>());
 }
 
 TEST(JsonVariant_As_Tests, ArrayAsString) {
@@ -212,7 +212,7 @@ TEST(JsonVariant_As_Tests, ArrayAsString) {
   arr.add(2);
 
   JsonVariant variant = arr;
-  ASSERT_EQ(String("[4,2]"), variant.as<String>());
+  ASSERT_EQ(std::string("[4,2]"), variant.as<std::string>());
 }
 
 TEST(JsonVariant_As_Tests, ArrayAsJsonArray) {

+ 69 - 62
test/ArduinoString_Tests.cpp → test/String_Tests.cpp

@@ -5,12 +5,12 @@
 // https://github.com/bblanchon/ArduinoJson
 // If you like this project, please add a star!
 
-#include <gtest/gtest.h>
 #include <ArduinoJson.h>
+#include <gtest/gtest.h>
 
-class ArduinoStringTests : public ::testing::Test {
+class StringTests : public ::testing::Test {
  protected:
-  static void eraseString(String &str) {
+  static void eraseString(std::string &str) {
     char *p = const_cast<char *>(str.c_str());
     while (*p) *p++ = '*';
   }
@@ -18,100 +18,100 @@ class ArduinoStringTests : public ::testing::Test {
   DynamicJsonBuffer _jsonBuffer;
 };
 
-TEST_F(ArduinoStringTests, JsonBuffer_ParseArray) {
-  String json("[\"hello\"]");
+TEST_F(StringTests, JsonBuffer_ParseArray) {
+  std::string json("[\"hello\"]");
   JsonArray &array = _jsonBuffer.parseArray(json);
   eraseString(json);
   ASSERT_TRUE(array.success());
   ASSERT_STREQ("hello", array[0]);
 }
 
-TEST_F(ArduinoStringTests, JsonBuffer_ParseObject) {
-  String json("{\"hello\":\"world\"}");
+TEST_F(StringTests, JsonBuffer_ParseObject) {
+  std::string json("{\"hello\":\"world\"}");
   JsonObject &object = _jsonBuffer.parseObject(json);
   eraseString(json);
   ASSERT_TRUE(object.success());
   ASSERT_STREQ("world", object["hello"]);
 }
 
-TEST_F(ArduinoStringTests, JsonObject_Subscript) {
+TEST_F(StringTests, JsonObject_Subscript) {
   char json[] = "{\"key\":\"value\"}";
   JsonObject &object = _jsonBuffer.parseObject(json);
-  ASSERT_STREQ("value", object[String("key")]);
+  ASSERT_STREQ("value", object[std::string("key")]);
 }
 
-TEST_F(ArduinoStringTests, JsonObject_ConstSubscript) {
+TEST_F(StringTests, JsonObject_ConstSubscript) {
   char json[] = "{\"key\":\"value\"}";
   const JsonObject &object = _jsonBuffer.parseObject(json);
-  ASSERT_STREQ("value", object[String("key")]);
+  ASSERT_STREQ("value", object[std::string("key")]);
 }
 
-TEST_F(ArduinoStringTests, JsonObject_SetKey) {
+TEST_F(StringTests, JsonObject_SetKey) {
   JsonObject &object = _jsonBuffer.createObject();
-  String key("hello");
+  std::string key("hello");
   object.set(key, "world");
   eraseString(key);
   ASSERT_STREQ("world", object["hello"]);
 }
 
-TEST_F(ArduinoStringTests, JsonObject_SetValue) {
+TEST_F(StringTests, JsonObject_SetValue) {
   JsonObject &object = _jsonBuffer.createObject();
-  String value("world");
+  std::string value("world");
   object.set("hello", value);
   eraseString(value);
   ASSERT_STREQ("world", object["hello"]);
 }
 
-TEST_F(ArduinoStringTests, JsonObject_SetKeyValue) {
+TEST_F(StringTests, JsonObject_SetKeyValue) {
   JsonObject &object = _jsonBuffer.createObject();
-  String key("hello");
-  String value("world");
+  std::string key("hello");
+  std::string value("world");
   object.set(key, value);
   eraseString(key);
   eraseString(value);
   ASSERT_STREQ("world", object["hello"]);
 }
 
-TEST_F(ArduinoStringTests, JsonObject_SetToArraySubscript) {
+TEST_F(StringTests, JsonObject_SetToArraySubscript) {
   JsonArray &arr = _jsonBuffer.createArray();
   arr.add("world");
 
   JsonObject &object = _jsonBuffer.createObject();
-  object.set(String("hello"), arr[0]);
+  object.set(std::string("hello"), arr[0]);
 
   ASSERT_STREQ("world", object["hello"]);
 }
 
-TEST_F(ArduinoStringTests, JsonObject_SetToObjectSubscript) {
+TEST_F(StringTests, JsonObject_SetToObjectSubscript) {
   JsonObject &arr = _jsonBuffer.createObject();
   arr.set("x", "world");
 
   JsonObject &object = _jsonBuffer.createObject();
-  object.set(String("hello"), arr["x"]);
+  object.set(std::string("hello"), arr["x"]);
 
   ASSERT_STREQ("world", object["hello"]);
 }
 
-TEST_F(ArduinoStringTests, JsonObject_Get) {
+TEST_F(StringTests, JsonObject_Get) {
   char json[] = "{\"key\":\"value\"}";
   const JsonObject &object = _jsonBuffer.parseObject(json);
-  ASSERT_STREQ("value", object.get(String("key")));
+  ASSERT_STREQ("value", object.get<const char *>(std::string("key")));
 }
 
-TEST_F(ArduinoStringTests, JsonObject_GetT) {
+TEST_F(StringTests, JsonObject_GetT) {
   char json[] = "{\"key\":\"value\"}";
   const JsonObject &object = _jsonBuffer.parseObject(json);
-  ASSERT_STREQ("value", object.get<const char *>(String("key")));
+  ASSERT_STREQ("value", object.get<const char *>(std::string("key")));
 }
 
-TEST_F(ArduinoStringTests, JsonObject_IsT) {
+TEST_F(StringTests, JsonObject_IsT) {
   char json[] = "{\"key\":\"value\"}";
   const JsonObject &object = _jsonBuffer.parseObject(json);
-  ASSERT_TRUE(object.is<const char *>(String("key")));
+  ASSERT_TRUE(object.is<const char *>(std::string("key")));
 }
 
-TEST_F(ArduinoStringTests, JsonObject_CreateNestedObject) {
-  String key = "key";
+TEST_F(StringTests, JsonObject_CreateNestedObject) {
+  std::string key = "key";
   char json[64];
   JsonObject &object = _jsonBuffer.createObject();
   object.createNestedObject(key);
@@ -120,8 +120,8 @@ TEST_F(ArduinoStringTests, JsonObject_CreateNestedObject) {
   ASSERT_STREQ("{\"key\":{}}", json);
 }
 
-TEST_F(ArduinoStringTests, JsonObject_CreateNestedArray) {
-  String key = "key";
+TEST_F(StringTests, JsonObject_CreateNestedArray) {
+  std::string key = "key";
   char json[64];
   JsonObject &object = _jsonBuffer.createObject();
   object.createNestedArray(key);
@@ -130,99 +130,99 @@ TEST_F(ArduinoStringTests, JsonObject_CreateNestedArray) {
   ASSERT_STREQ("{\"key\":[]}", json);
 }
 
-TEST_F(ArduinoStringTests, JsonObject_ContainsKey) {
+TEST_F(StringTests, JsonObject_ContainsKey) {
   char json[] = "{\"key\":\"value\"}";
   const JsonObject &object = _jsonBuffer.parseObject(json);
-  ASSERT_TRUE(object.containsKey(String("key")));
+  ASSERT_TRUE(object.containsKey(std::string("key")));
 }
 
-TEST_F(ArduinoStringTests, JsonObject_Remove) {
+TEST_F(StringTests, JsonObject_Remove) {
   char json[] = "{\"key\":\"value\"}";
   JsonObject &object = _jsonBuffer.parseObject(json);
   ASSERT_EQ(1, object.size());
-  object.remove(String("key"));
+  object.remove(std::string("key"));
   ASSERT_EQ(0, object.size());
 }
 
-TEST_F(ArduinoStringTests, JsonObjectSubscript_SetKey) {
+TEST_F(StringTests, JsonObjectSubscript_SetKey) {
   JsonObject &object = _jsonBuffer.createObject();
-  String key("hello");
+  std::string key("hello");
   object[key] = "world";
   eraseString(key);
   ASSERT_STREQ("world", object["hello"]);
 }
 
-TEST_F(ArduinoStringTests, JsonObjectSubscript_SetValue) {
+TEST_F(StringTests, JsonObjectSubscript_SetValue) {
   JsonObject &object = _jsonBuffer.createObject();
-  String value("world");
+  std::string value("world");
   object["hello"] = value;
   eraseString(value);
   ASSERT_STREQ("world", object["hello"]);
 }
 
-TEST_F(ArduinoStringTests, JsonArray_Add) {
+TEST_F(StringTests, JsonArray_Add) {
   JsonArray &array = _jsonBuffer.createArray();
-  String value("hello");
+  std::string value("hello");
   array.add(value);
   eraseString(value);
   ASSERT_STREQ("hello", array[0]);
 }
 
-TEST_F(ArduinoStringTests, JsonArray_Set) {
+TEST_F(StringTests, JsonArray_Set) {
   JsonArray &array = _jsonBuffer.createArray();
-  String value("world");
+  std::string value("world");
   array.add("hello");
   array.set(0, value);
   eraseString(value);
   ASSERT_STREQ("world", array[0]);
 }
 
-TEST_F(ArduinoStringTests, JsonArraySubscript) {
+TEST_F(StringTests, JsonArraySubscript) {
   JsonArray &array = _jsonBuffer.createArray();
-  String value("world");
+  std::string value("world");
   array.add("hello");
   array[0] = value;
   eraseString(value);
   ASSERT_STREQ("world", array[0]);
 }
 
-TEST_F(ArduinoStringTests, JsonArray_PrintTo) {
+TEST_F(StringTests, JsonArray_PrintTo) {
   JsonArray &array = _jsonBuffer.createArray();
   array.add(4);
   array.add(2);
-  String json;
+  std::string json;
   array.printTo(json);
-  ASSERT_EQ(String("[4,2]"), json);
+  ASSERT_EQ(std::string("[4,2]"), json);
 }
 
-TEST_F(ArduinoStringTests, JsonArray_PrettyPrintTo) {
+TEST_F(StringTests, JsonArray_PrettyPrintTo) {
   JsonArray &array = _jsonBuffer.createArray();
   array.add(4);
   array.add(2);
-  String json;
+  std::string json;
   array.prettyPrintTo(json);
-  ASSERT_EQ(String("[\r\n  4,\r\n  2\r\n]"), json);
+  ASSERT_EQ(std::string("[\r\n  4,\r\n  2\r\n]"), json);
 }
 
-TEST_F(ArduinoStringTests, JsonObject_PrintTo) {
+TEST_F(StringTests, JsonObject_PrintTo) {
   JsonObject &object = _jsonBuffer.createObject();
   object["key"] = "value";
-  String json;
+  std::string json;
   object.printTo(json);
-  ASSERT_EQ(String("{\"key\":\"value\"}"), json);
+  ASSERT_EQ(std::string("{\"key\":\"value\"}"), json);
 }
 
-TEST_F(ArduinoStringTests, JsonObject_PrettyPrintTo) {
+TEST_F(StringTests, JsonObject_PrettyPrintTo) {
   JsonObject &object = _jsonBuffer.createObject();
   object["key"] = "value";
-  String json;
+  std::string json;
   object.prettyPrintTo(json);
-  ASSERT_EQ(String("{\r\n  \"key\": \"value\"\r\n}"), json);
+  ASSERT_EQ(std::string("{\r\n  \"key\": \"value\"\r\n}"), json);
 }
 
-TEST_F(ArduinoStringTests, JsonBuffer_GrowWhenAddingNewKey) {
+TEST_F(StringTests, JsonBuffer_GrowWhenAddingNewKey) {
   JsonObject &object = _jsonBuffer.createObject();
-  String key1("hello"), key2("world");
+  std::string key1("hello"), key2("world");
 
   object[key1] = 1;
   size_t sizeBefore = _jsonBuffer.size();
@@ -232,9 +232,9 @@ TEST_F(ArduinoStringTests, JsonBuffer_GrowWhenAddingNewKey) {
   ASSERT_GT(sizeAfter - sizeBefore, key2.size());
 }
 
-TEST_F(ArduinoStringTests, JsonBuffer_DontGrowWhenReusingKey) {
+TEST_F(StringTests, JsonBuffer_DontGrowWhenReusingKey) {
   JsonObject &object = _jsonBuffer.createObject();
-  String key("hello");
+  std::string key("hello");
 
   object[key] = 1;
   size_t sizeBefore = _jsonBuffer.size();
@@ -243,3 +243,10 @@ TEST_F(ArduinoStringTests, JsonBuffer_DontGrowWhenReusingKey) {
 
   ASSERT_EQ(sizeBefore, sizeAfter);
 }
+
+TEST_F(StringTests, JsonBuffer_strdup) {
+  std::string original("hello");
+  char *copy = _jsonBuffer.strdup(original);
+  original[0] = 'w';
+  ASSERT_STREQ("hello", copy);
+}