Parcourir la source

Added support for custom converters (closes #687)

Benoit Blanchon il y a 5 ans
Parent
commit
d7f5b56ca4

+ 1 - 0
CHANGELOG.md

@@ -4,6 +4,7 @@ ArduinoJson: change log
 HEAD
 ----
 
+* Added support for custom converters (issue #687)
 * Removed support for `char` values, see below (issue #1498)
 * `deserializeJson()` leaves `\uXXXX` unchanged instead of returning `NotSupported`
 * `deserializeMsgPack()` inserts `null` instead of returning `NotSupported`

+ 1 - 0
README.md

@@ -38,6 +38,7 @@ ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things).
     * Supports Arduino's `Stream` and [STL's `std::istream`/`std::ostream`](https://arduinojson.org/v6/api/config/enable_std_stream/?utm_source=github&utm_medium=readme)
     * [Supports Flash strings](https://arduinojson.org/v6/api/config/enable_progmem/?utm_source=github&utm_medium=readme)
     * Supports [custom readers](https://arduinojson.org/v6/api/json/deserializejson/?utm_source=github&utm_medium=readme#custom-reader) and [custom writers](https://arduinojson.org/v6/api/json/serializejson/?utm_source=github&utm_medium=readme#custom-writer)
+    * Supports custom converters
 * Portable
     * Usable on any C++ project (not limited to Arduino)
     * Compatible with C++98

+ 1 - 0
extras/tests/JsonVariant/CMakeLists.txt

@@ -9,6 +9,7 @@ add_executable(JsonVariantTests
 	compare.cpp
 	containsKey.cpp
 	copy.cpp
+	converters.cpp
 	createNested.cpp
 	is.cpp
 	isnull.cpp

+ 144 - 0
extras/tests/JsonVariant/converters.cpp

@@ -0,0 +1,144 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2021
+// MIT License
+
+#include <ArduinoJson.h>
+#include <stdint.h>
+#include <catch.hpp>
+
+namespace {
+struct Date {
+  int day;
+  int month;
+  int year;
+};
+
+bool convertToJson(JsonVariant variant, const Date& date) {
+  variant["day"] = date.day;
+  variant["month"] = date.month;
+  variant["year"] = date.year;
+  return true;
+}
+
+void convertFromJson(Date& date, JsonVariantConst variant) {
+  date.day = variant["day"];
+  date.month = variant["month"];
+  date.year = variant["year"];
+}
+
+bool canConvertFromJson(Date&, JsonVariantConst variant) {
+  return variant["day"].is<int>() && variant["month"].is<int>() &&
+         variant["year"].is<int>();
+}
+}  // namespace
+
+TEST_CASE("Custom converter with overloading") {
+  DynamicJsonDocument doc(4096);
+
+  SECTION("convert JSON to Date") {
+    doc["date"]["day"] = 2;
+    doc["date"]["month"] = 3;
+    doc["date"]["year"] = 2021;
+
+    Date date = doc["date"];
+
+    REQUIRE(date.day == 2);
+    REQUIRE(date.month == 3);
+    REQUIRE(date.year == 2021);
+  }
+
+  SECTION("is<Date>() returns true") {
+    doc["date"]["day"] = 2;
+    doc["date"]["month"] = 3;
+    doc["date"]["year"] = 2021;
+
+    REQUIRE(doc["date"].is<Date>());
+  }
+
+  SECTION("is<Date>() returns false") {
+    doc["date"]["day"] = 2;
+    doc["date"]["month"] = 3;
+    doc["date"]["year"] = "2021";
+
+    REQUIRE(doc["date"].is<Date>() == false);
+  }
+
+  SECTION("convert Date to JSON") {
+    Date date = {19, 3, 2021};
+    doc["date"] = date;
+
+    REQUIRE(doc["date"]["day"] == 19);
+    REQUIRE(doc["date"]["month"] == 3);
+    REQUIRE(doc["date"]["year"] == 2021);
+  }
+}
+
+class Complex {
+ public:
+  explicit Complex(double r, double i) : _real(r), _imag(i) {}
+
+  double real() const {
+    return _real;
+  }
+
+  double imag() const {
+    return _imag;
+  }
+
+ private:
+  double _real, _imag;
+};
+
+namespace ARDUINOJSON_NAMESPACE {
+template <>
+struct Converter<Complex> {
+  static bool toJson(VariantRef variant, const Complex& value) {
+    variant["real"] = value.real();
+    variant["imag"] = value.imag();
+    return true;
+  }
+
+  static Complex fromJson(VariantConstRef variant) {
+    return Complex(variant["real"], variant["imag"]);
+  }
+
+  static bool checkJson(VariantConstRef variant) {
+    return variant["real"].is<double>() && variant["imag"].is<double>();
+  }
+};
+}  // namespace ARDUINOJSON_NAMESPACE
+
+TEST_CASE("Custom converter with specialization") {
+  DynamicJsonDocument doc(4096);
+
+  SECTION("convert JSON to Complex") {
+    doc["value"]["real"] = 2;
+    doc["value"]["imag"] = 3;
+
+    Complex value = doc["value"];
+
+    REQUIRE(value.real() == 2);
+    REQUIRE(value.imag() == 3);
+  }
+
+  SECTION("is<Complex>() returns true") {
+    doc["value"]["real"] = 2;
+    doc["value"]["imag"] = 3;
+
+    REQUIRE(doc["value"].is<Complex>());
+  }
+
+  SECTION("is<Complex>() returns false") {
+    doc["value"]["real"] = 2;
+    doc["value"]["imag"] = "3";
+
+    REQUIRE(doc["value"].is<Complex>() == false);
+  }
+
+  SECTION("convert value to JSON") {
+    doc["value"] = Complex(19, 3);
+
+    REQUIRE(doc["value"]["real"] == 19);
+    REQUIRE(doc["value"]["imag"] == 3);
+  }
+}

+ 18 - 18
extras/tests/JsonVariant/is.cpp

@@ -19,7 +19,7 @@ TEST_CASE("JsonVariant::is<T>()") {
     CHECK(variant.is<JsonVariant>() == false);
     CHECK(variant.is<JsonVariantConst>() == false);
     CHECK(variant.is<bool>() == false);
-    CHECK(variant.is<char *>() == false);
+    CHECK(variant.is<const char *>() == false);
     CHECK(variant.is<int>() == false);
     CHECK(variant.is<std::string>() == false);
     CHECK(variant.is<float>() == false);
@@ -32,7 +32,7 @@ TEST_CASE("JsonVariant::is<T>()") {
     CHECK(variant.is<JsonObject>() == false);
     CHECK(variant.is<JsonArray>() == false);
     CHECK(variant.is<bool>() == false);
-    CHECK(variant.is<char *>() == false);
+    CHECK(variant.is<const char *>() == false);
     CHECK(variant.is<int>() == false);
     CHECK(variant.is<std::string>() == false);
     CHECK(variant.is<float>() == false);
@@ -47,7 +47,7 @@ TEST_CASE("JsonVariant::is<T>()") {
     CHECK(variant.is<JsonVariantConst>() == true);
     CHECK(variant.is<JsonObject>() == false);
     CHECK(variant.is<JsonArray>() == false);
-    CHECK(variant.is<char *>() == false);
+    CHECK(variant.is<const char *>() == false);
     CHECK(variant.is<int>() == false);
     CHECK(variant.is<std::string>() == false);
     CHECK(variant.is<float>() == false);
@@ -62,7 +62,7 @@ TEST_CASE("JsonVariant::is<T>()") {
     CHECK(variant.is<JsonVariantConst>() == true);
     CHECK(variant.is<JsonObject>() == false);
     CHECK(variant.is<JsonArray>() == false);
-    CHECK(variant.is<char *>() == false);
+    CHECK(variant.is<const char *>() == false);
     CHECK(variant.is<int>() == false);
     CHECK(variant.is<std::string>() == false);
     CHECK(variant.is<float>() == false);
@@ -83,7 +83,7 @@ TEST_CASE("JsonVariant::is<T>()") {
     CHECK(variant.is<bool>() == false);
     CHECK(variant.is<JsonObject>() == false);
     CHECK(variant.is<JsonArray>() == false);
-    CHECK(variant.is<char *>() == false);
+    CHECK(variant.is<const char *>() == false);
     CHECK(variant.is<std::string>() == false);
   }
 
@@ -97,7 +97,7 @@ TEST_CASE("JsonVariant::is<T>()") {
     CHECK(variant.is<bool>() == false);
     CHECK(variant.is<JsonObject>() == false);
     CHECK(variant.is<JsonArray>() == false);
-    CHECK(variant.is<char *>() == false);
+    CHECK(variant.is<const char *>() == false);
     CHECK(variant.is<int>() == false);
     CHECK(variant.is<std::string>() == false);
     CHECK(variant.is<MYENUM2>() == false);
@@ -106,7 +106,7 @@ TEST_CASE("JsonVariant::is<T>()") {
   SECTION("const char*") {
     variant.set("4.2");
 
-    CHECK(variant.is<char *>() == true);
+    CHECK(variant.is<const char *>() == true);
     CHECK(variant.is<const char *>() == true);
     CHECK(variant.is<std::string>() == true);
     CHECK(variant.is<JsonVariant>() == true);
@@ -132,7 +132,7 @@ TEST_CASE("JsonVariant::is<T>()") {
     CHECK(variant.is<int>() == false);
     CHECK(variant.is<float>() == false);
     CHECK(variant.is<bool>() == false);
-    CHECK(variant.is<char *>() == false);
+    CHECK(variant.is<const char *>() == false);
     CHECK(variant.is<MYENUM2>() == false);
   }
 
@@ -148,7 +148,7 @@ TEST_CASE("JsonVariant::is<T>()") {
     CHECK(variant.is<int>() == false);
     CHECK(variant.is<float>() == false);
     CHECK(variant.is<bool>() == false);
-    CHECK(variant.is<char *>() == false);
+    CHECK(variant.is<const char *>() == false);
     CHECK(variant.is<MYENUM2>() == false);
     CHECK(variant.is<JsonVariant>() == true);
     CHECK(variant.is<JsonVariantConst>() == true);
@@ -170,7 +170,7 @@ TEST_CASE("JsonVariantConst::is<T>()") {
     CHECK(cvariant.is<JsonVariant>() == false);
     CHECK(cvariant.is<JsonVariantConst>() == false);
     CHECK(cvariant.is<bool>() == false);
-    CHECK(cvariant.is<char *>() == false);
+    CHECK(cvariant.is<const char *>() == false);
     CHECK(cvariant.is<int>() == false);
     CHECK(cvariant.is<std::string>() == false);
     CHECK(cvariant.is<float>() == false);
@@ -183,7 +183,7 @@ TEST_CASE("JsonVariantConst::is<T>()") {
     CHECK(cvariant.is<JsonArray>() == false);
     CHECK(cvariant.is<JsonVariant>() == false);
     CHECK(cvariant.is<bool>() == false);
-    CHECK(cvariant.is<char *>() == false);
+    CHECK(cvariant.is<const char *>() == false);
     CHECK(cvariant.is<int>() == false);
     CHECK(cvariant.is<std::string>() == false);
     CHECK(cvariant.is<float>() == false);
@@ -198,7 +198,7 @@ TEST_CASE("JsonVariantConst::is<T>()") {
     CHECK(cvariant.is<JsonVariant>() == false);
     CHECK(cvariant.is<JsonObject>() == false);
     CHECK(cvariant.is<JsonArray>() == false);
-    CHECK(cvariant.is<char *>() == false);
+    CHECK(cvariant.is<const char *>() == false);
     CHECK(cvariant.is<int>() == false);
     CHECK(cvariant.is<std::string>() == false);
     CHECK(cvariant.is<float>() == false);
@@ -213,7 +213,7 @@ TEST_CASE("JsonVariantConst::is<T>()") {
     CHECK(cvariant.is<JsonVariant>() == false);
     CHECK(cvariant.is<JsonObject>() == false);
     CHECK(cvariant.is<JsonArray>() == false);
-    CHECK(cvariant.is<char *>() == false);
+    CHECK(cvariant.is<const char *>() == false);
     CHECK(cvariant.is<int>() == false);
     CHECK(cvariant.is<std::string>() == false);
     CHECK(cvariant.is<float>() == false);
@@ -234,7 +234,7 @@ TEST_CASE("JsonVariantConst::is<T>()") {
     CHECK(cvariant.is<JsonObject>() == false);
     CHECK(cvariant.is<JsonArray>() == false);
     CHECK(cvariant.is<JsonVariant>() == false);
-    CHECK(cvariant.is<char *>() == false);
+    CHECK(cvariant.is<const char *>() == false);
     CHECK(cvariant.is<std::string>() == false);
   }
 
@@ -248,7 +248,7 @@ TEST_CASE("JsonVariantConst::is<T>()") {
     CHECK(cvariant.is<JsonObject>() == false);
     CHECK(cvariant.is<JsonArray>() == false);
     CHECK(cvariant.is<JsonVariant>() == false);
-    CHECK(cvariant.is<char *>() == false);
+    CHECK(cvariant.is<const char *>() == false);
     CHECK(cvariant.is<int>() == false);
     CHECK(cvariant.is<std::string>() == false);
     CHECK(cvariant.is<MYENUM2>() == false);
@@ -257,7 +257,7 @@ TEST_CASE("JsonVariantConst::is<T>()") {
   SECTION("const char*") {
     variant.set("4.2");
 
-    CHECK(cvariant.is<char *>() == true);
+    CHECK(cvariant.is<const char *>() == true);
     CHECK(cvariant.is<const char *>() == true);
     CHECK(cvariant.is<std::string>() == true);
     CHECK(cvariant.is<double>() == false);
@@ -282,7 +282,7 @@ TEST_CASE("JsonVariantConst::is<T>()") {
     CHECK(cvariant.is<int>() == false);
     CHECK(cvariant.is<float>() == false);
     CHECK(cvariant.is<bool>() == false);
-    CHECK(cvariant.is<char *>() == false);
+    CHECK(cvariant.is<const char *>() == false);
     CHECK(cvariant.is<MYENUM2>() == false);
   }
 
@@ -298,7 +298,7 @@ TEST_CASE("JsonVariantConst::is<T>()") {
     CHECK(cvariant.is<int>() == false);
     CHECK(cvariant.is<float>() == false);
     CHECK(cvariant.is<bool>() == false);
-    CHECK(cvariant.is<char *>() == false);
+    CHECK(cvariant.is<const char *>() == false);
     CHECK(cvariant.is<MYENUM2>() == false);
   }
 }

+ 2 - 2
extras/tests/JsonVariant/undefined.cpp

@@ -47,8 +47,8 @@ TEST_CASE("JsonVariant undefined") {
       REQUIRE(variant.is<unsigned>() == false);
     }
 
-    SECTION("char*") {
-      REQUIRE(variant.is<char*>() == false);
+    SECTION("const char*") {
+      REQUIRE(variant.is<const char*>() == false);
     }
 
     SECTION("double") {

+ 1 - 1
extras/tests/Misc/Readers.cpp

@@ -3,7 +3,7 @@
 // MIT License
 
 #define ARDUINOJSON_ENABLE_ARDUINO_STREAM 1
-#include <ArduinoJson/Deserialization/Reader.hpp>
+#include <ArduinoJson.hpp>
 #include <catch.hpp>
 
 using namespace ARDUINOJSON_NAMESPACE;

+ 1 - 2
extras/tests/Numbers/parseDouble.cpp

@@ -6,8 +6,7 @@
 #define ARDUINOJSON_ENABLE_NAN 1
 #define ARDUINOJSON_ENABLE_INFINITY 1
 
-#include <ArduinoJson/Numbers/parseNumber.hpp>
-#include <ArduinoJson/Variant/VariantImpl.hpp>
+#include <ArduinoJson.hpp>
 #include <catch.hpp>
 
 using namespace ARDUINOJSON_NAMESPACE;

+ 1 - 2
extras/tests/Numbers/parseFloat.cpp

@@ -6,8 +6,7 @@
 #define ARDUINOJSON_ENABLE_NAN 1
 #define ARDUINOJSON_ENABLE_INFINITY 1
 
-#include <ArduinoJson/Numbers/parseNumber.hpp>
-#include <ArduinoJson/Variant/VariantImpl.hpp>
+#include <ArduinoJson.hpp>
 #include <catch.hpp>
 
 using namespace ARDUINOJSON_NAMESPACE;

+ 1 - 2
extras/tests/Numbers/parseInteger.cpp

@@ -3,8 +3,7 @@
 // MIT License
 
 #include <stdint.h>
-#include <ArduinoJson/Numbers/parseNumber.hpp>
-#include <ArduinoJson/Variant/VariantImpl.hpp>
+#include <ArduinoJson.hpp>
 #include <catch.hpp>
 
 using namespace ARDUINOJSON_NAMESPACE;

+ 1 - 3
extras/tests/Numbers/parseNumber.cpp

@@ -2,9 +2,7 @@
 // Copyright Benoit Blanchon 2014-2021
 // MIT License
 
-#include <ArduinoJson/Numbers/Integer.hpp>
-#include <ArduinoJson/Numbers/parseNumber.hpp>
-#include <ArduinoJson/Variant/VariantImpl.hpp>
+#include <ArduinoJson.hpp>
 #include <catch.hpp>
 
 using namespace ARDUINOJSON_NAMESPACE;

+ 1 - 1
src/ArduinoJson.hpp

@@ -27,7 +27,7 @@
 #include "ArduinoJson/Collection/CollectionImpl.hpp"
 #include "ArduinoJson/Object/MemberProxy.hpp"
 #include "ArduinoJson/Object/ObjectImpl.hpp"
-#include "ArduinoJson/Variant/VariantAsImpl.hpp"
+#include "ArduinoJson/Variant/ConverterImpl.hpp"
 #include "ArduinoJson/Variant/VariantCompare.hpp"
 #include "ArduinoJson/Variant/VariantImpl.hpp"
 

+ 38 - 0
src/ArduinoJson/Array/ArrayRef.hpp

@@ -164,4 +164,42 @@ class ArrayRef : public ArrayRefBase<CollectionData>,
  private:
   MemoryPool* _pool;
 };
+
+template <>
+struct Converter<ArrayConstRef> {
+  static bool toJson(VariantRef variant, VariantConstRef value) {
+    return variantCopyFrom(getData(variant), getData(value), getPool(variant));
+  }
+
+  static ArrayConstRef fromJson(VariantConstRef variant) {
+    return ArrayConstRef(variantAsArray(getData(variant)));
+  }
+
+  static bool checkJson(VariantConstRef variant) {
+    const VariantData* data = getData(variant);
+    return data && data->isArray();
+  }
+};
+
+template <>
+struct Converter<ArrayRef> {
+  static bool toJson(VariantRef variant, VariantConstRef value) {
+    return variantCopyFrom(getData(variant), getData(value), getPool(variant));
+  }
+
+  static ArrayRef fromJson(VariantRef variant) {
+    VariantData* data = getData(variant);
+    MemoryPool* pool = getPool(variant);
+    return ArrayRef(pool, data != 0 ? data->asArray() : 0);
+  }
+
+  static bool checkJson(VariantConstRef) {
+    return false;
+  }
+
+  static bool checkJson(VariantRef variant) {
+    VariantData* data = getData(variant);
+    return data && data->isArray();
+  }
+};
 }  // namespace ARDUINOJSON_NAMESPACE

+ 2 - 0
src/ArduinoJson/Array/ArrayShortcuts.hpp

@@ -9,6 +9,8 @@
 
 namespace ARDUINOJSON_NAMESPACE {
 // Forward declarations.
+class ArrayRef;
+class ObjectRef;
 template <typename>
 class ElementProxy;
 

+ 4 - 0
src/ArduinoJson/Array/ElementProxy.hpp

@@ -170,6 +170,10 @@ class ElementProxy : public VariantOperators<ElementProxy<TArray> >,
     return _array.getOrAddElement(_index);
   }
 
+  friend bool convertToJson(VariantRef variant, const this_type& value) {
+    return variant.set(value.getUpstreamElement());
+  }
+
   TArray _array;
   const size_t _index;
 };

+ 2 - 1
src/ArduinoJson/Array/Utilities.hpp

@@ -76,7 +76,8 @@ class ArrayCopier1D : public Visitor<size_t> {
     VariantSlot* slot = array.head();
 
     while (slot != 0 && size < _capacity) {
-      _destination[size++] = variantAs<T>(slot->data());
+      _destination[size++] =
+          Converter<T>::fromJson(VariantConstRef(slot->data()));
       slot = slot->next();
     }
     return size;

+ 4 - 0
src/ArduinoJson/Document/JsonDocument.hpp

@@ -337,4 +337,8 @@ class JsonDocument : public Visitable {
   JsonDocument& operator=(const JsonDocument&);
 };
 
+inline bool convertToJson(VariantRef variant, const JsonDocument& doc) {
+  return variant.set(doc.as<VariantConstRef>());
+}
+
 }  // namespace ARDUINOJSON_NAMESPACE

+ 2 - 2
src/ArduinoJson/Numbers/parseNumber.hpp

@@ -10,7 +10,7 @@
 #include <ArduinoJson/Polyfills/ctype.hpp>
 #include <ArduinoJson/Polyfills/math.hpp>
 #include <ArduinoJson/Polyfills/type_traits.hpp>
-#include <ArduinoJson/Variant/VariantAs.hpp>
+#include <ArduinoJson/Variant/Converter.hpp>
 #include <ArduinoJson/Variant/VariantData.hpp>
 
 namespace ARDUINOJSON_NAMESPACE {
@@ -142,6 +142,6 @@ inline T parseNumber(const char* s) {
   VariantData value;
   value.init();  // VariantData is a POD, so it has no constructor
   parseNumber(s, value);
-  return variantAs<T>(&value);
+  return Converter<T>::fromJson(VariantConstRef(&value));
 }
 }  // namespace ARDUINOJSON_NAMESPACE

+ 4 - 0
src/ArduinoJson/Object/MemberProxy.hpp

@@ -193,6 +193,10 @@ class MemberProxy : public VariantOperators<MemberProxy<TObject, TStringRef> >,
     return _object.getOrAddMember(_key);
   }
 
+  friend bool convertToJson(VariantRef variant, const this_type &value) {
+    return variant.set(value.getUpstreamMember());
+  }
+
   TObject _object;
   TStringRef _key;
 };

+ 38 - 0
src/ArduinoJson/Object/ObjectRef.hpp

@@ -236,4 +236,42 @@ class ObjectRef : public ObjectRefBase<CollectionData>,
  private:
   MemoryPool* _pool;
 };
+
+template <>
+struct Converter<ObjectConstRef> {
+  static bool toJson(VariantRef variant, VariantConstRef value) {
+    return variantCopyFrom(getData(variant), getData(value), getPool(variant));
+  }
+
+  static ObjectConstRef fromJson(VariantConstRef variant) {
+    return ObjectConstRef(variantAsObject(getData(variant)));
+  }
+
+  static bool checkJson(VariantConstRef variant) {
+    const VariantData* data = getData(variant);
+    return data && data->isObject();
+  }
+};
+
+template <>
+struct Converter<ObjectRef> {
+  static bool toJson(VariantRef variant, VariantConstRef value) {
+    return variantCopyFrom(getData(variant), getData(value), getPool(variant));
+  }
+
+  static ObjectRef fromJson(VariantRef variant) {
+    VariantData* data = getData(variant);
+    MemoryPool* pool = getPool(variant);
+    return ObjectRef(pool, data != 0 ? data->asObject() : 0);
+  }
+
+  static bool checkJson(VariantConstRef) {
+    return false;
+  }
+
+  static bool checkJson(VariantRef variant) {
+    VariantData* data = getData(variant);
+    return data && data->isObject();
+  }
+};
 }  // namespace ARDUINOJSON_NAMESPACE

+ 12 - 0
src/ArduinoJson/Variant/Converter.hpp

@@ -0,0 +1,12 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2021
+// MIT License
+
+#pragma once
+
+namespace ARDUINOJSON_NAMESPACE {
+
+template <typename T, typename Enable = void>
+struct Converter;
+
+}  // namespace ARDUINOJSON_NAMESPACE

+ 209 - 0
src/ArduinoJson/Variant/ConverterImpl.hpp

@@ -0,0 +1,209 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2021
+// MIT License
+
+#pragma once
+
+#include <ArduinoJson/Strings/IsWriteableString.hpp>
+#include <ArduinoJson/Variant/VariantFunctions.hpp>
+#include <ArduinoJson/Variant/VariantRef.hpp>
+
+namespace ARDUINOJSON_NAMESPACE {
+
+template <typename T, typename Enable>
+struct Converter {
+  static bool toJson(VariantRef variant, const T& value) {
+    // clang-format off
+    return convertToJson(variant, value); // Error here? See https://arduinojson.org/v6/unsupported-set/
+    // clang-format on
+  }
+
+  static T fromJson(VariantConstRef variant) {
+    // clang-format off
+    T value; // Error here? See https://arduinojson.org/v6/non-default-constructible/
+    convertFromJson(value, variant);  // Error here? See https://arduinojson.org/v6/unsupported-as/
+    // clang-format on
+    return value;
+  }
+
+  static bool checkJson(VariantConstRef variant) {
+    T dummy;
+    // clang-format off
+    return canConvertFromJson(dummy, variant);  // Error here? See https://arduinojson.org/v6/unsupported-is/
+    // clang-format on
+  }
+};
+
+template <typename T>
+struct Converter<
+    T, typename enable_if<is_integral<T>::value && !is_same<bool, T>::value &&
+                          !is_same<char, T>::value>::type> {
+  static bool toJson(VariantRef variant, T value) {
+    VariantData* data = getData(variant);
+    ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T);
+    if (!data)
+      return false;
+    data->setInteger(value);
+    return true;
+  }
+
+  static T fromJson(VariantConstRef variant) {
+    ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T);
+    const VariantData* data = getData(variant);
+    return data ? data->asIntegral<T>() : T();
+  }
+
+  static bool checkJson(VariantConstRef variant) {
+    const VariantData* data = getData(variant);
+    return data && data->isInteger<T>();
+  }
+};
+
+template <typename T>
+struct Converter<T, typename enable_if<is_enum<T>::value>::type> {
+  static bool toJson(VariantRef variant, T value) {
+    return variant.set(static_cast<Integer>(value));
+  }
+
+  static T fromJson(VariantConstRef variant) {
+    const VariantData* data = getData(variant);
+    return data ? static_cast<T>(data->asIntegral<int>()) : T();
+  }
+
+  static bool checkJson(VariantConstRef variant) {
+    const VariantData* data = getData(variant);
+    return data && data->isInteger<int>();
+  }
+};
+
+template <>
+struct Converter<bool> {
+  static bool toJson(VariantRef variant, bool value) {
+    VariantData* data = getData(variant);
+    if (!data)
+      return false;
+    data->setBoolean(value);
+    return true;
+  }
+
+  static bool fromJson(VariantConstRef variant) {
+    const VariantData* data = getData(variant);
+    return data ? data->asBoolean() : false;
+  }
+
+  static bool checkJson(VariantConstRef variant) {
+    const VariantData* data = getData(variant);
+    return data && data->isBoolean();
+  }
+};
+
+template <typename T>
+struct Converter<T, typename enable_if<is_floating_point<T>::value>::type> {
+  static bool toJson(VariantRef variant, T value) {
+    VariantData* data = getData(variant);
+    if (!data)
+      return false;
+    data->setFloat(static_cast<Float>(value));
+    return true;
+  }
+
+  static T fromJson(VariantConstRef variant) {
+    const VariantData* data = getData(variant);
+    return data ? data->asFloat<T>() : false;
+  }
+
+  static bool checkJson(VariantConstRef variant) {
+    const VariantData* data = getData(variant);
+    return data && data->isFloat();
+  }
+};
+
+template <>
+struct Converter<const char*> {
+  static bool toJson(VariantRef variant, const char* value) {
+    // TODO: don't pass pool
+    return variantSetString(getData(variant), adaptString(value),
+                            getPool(variant));
+  }
+
+  static const char* fromJson(VariantConstRef variant) {
+    const VariantData* data = getData(variant);
+    return data ? data->asString() : 0;
+  }
+
+  static bool checkJson(VariantConstRef variant) {
+    const VariantData* data = getData(variant);
+    return data && data->isString();
+  }
+};
+
+template <typename T>
+inline typename enable_if<IsString<T>::value, bool>::type convertToJson(
+    VariantRef variant, const T& value) {
+  VariantData* data = getData(variant);
+  MemoryPool* pool = getPool(variant);
+  return variantSetString(data, adaptString(value), pool);
+}
+
+template <typename T>
+inline typename enable_if<IsWriteableString<T>::value>::type convertFromJson(
+    T& value, VariantConstRef variant) {
+  const VariantData* data = getData(variant);
+  const char* cstr = data != 0 ? data->asString() : 0;
+  if (cstr)
+    value = cstr;
+  else
+    serializeJson(variant, value);
+}
+
+template <typename T>
+inline typename enable_if<IsWriteableString<T>::value, bool>::type
+canConvertFromJson(T&, VariantConstRef variant) {
+  const VariantData* data = getData(variant);
+  return data && data->isString();
+}
+
+template <>
+struct Converter<SerializedValue<const char*> > {
+  static bool toJson(VariantRef variant, SerializedValue<const char*> value) {
+    VariantData* data = getData(variant);
+    if (!data)
+      return false;
+    data->setLinkedRaw(value);
+    return true;
+  }
+};
+
+// SerializedValue<std::string>
+// SerializedValue<String>
+// SerializedValue<const __FlashStringHelper*>
+template <typename T>
+struct Converter<SerializedValue<T>,
+                 typename enable_if<!is_same<const char*, T>::value>::type> {
+  static bool toJson(VariantRef variant, SerializedValue<T> value) {
+    VariantData* data = getData(variant);
+    MemoryPool* pool = getPool(variant);
+    return data != 0 && data->setOwnedRaw(value, pool);
+  }
+};
+
+#if ARDUINOJSON_HAS_NULLPTR
+
+template <>
+struct Converter<decltype(nullptr)> {
+  static bool toJson(VariantRef variant, decltype(nullptr)) {
+    variantSetNull(getData(variant));
+    return true;
+  }
+  static decltype(nullptr) fromJson(VariantConstRef) {
+    return nullptr;
+  }
+  static bool checkJson(VariantConstRef variant) {
+    const VariantData* data = getData(variant);
+    return data == 0 || data->isNull();
+  }
+};
+
+#endif
+
+}  // namespace ARDUINOJSON_NAMESPACE

+ 0 - 76
src/ArduinoJson/Variant/VariantAs.hpp

@@ -1,76 +0,0 @@
-// ArduinoJson - arduinojson.org
-// Copyright Benoit Blanchon 2014-2021
-// MIT License
-
-#pragma once
-
-#include <ArduinoJson/Strings/IsWriteableString.hpp>
-#include <ArduinoJson/Variant/VariantData.hpp>
-
-namespace ARDUINOJSON_NAMESPACE {
-
-class ArrayRef;
-class ArrayConstRef;
-class ObjectRef;
-class ObjectConstRef;
-class VariantRef;
-class VariantConstRef;
-
-template <typename T>
-inline typename enable_if<is_integral<T>::value && !is_same<bool, T>::value &&
-                              !is_same<char, T>::value,
-                          T>::type
-variantAs(const VariantData* data) {
-  ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T);
-  return data != 0 ? data->asIntegral<T>() : T(0);
-}
-
-template <typename T>
-inline typename enable_if<is_enum<T>::value, T>::type variantAs(
-    const VariantData* data) {
-  return data != 0 ? static_cast<T>(data->asIntegral<int>()) : T();
-}
-
-template <typename T>
-inline typename enable_if<is_same<T, bool>::value, T>::type variantAs(
-    const VariantData* data) {
-  return data != 0 ? data->asBoolean() : false;
-}
-
-template <typename T>
-inline typename enable_if<is_floating_point<T>::value, T>::type variantAs(
-    const VariantData* data) {
-  return data != 0 ? data->asFloat<T>() : T(0);
-}
-
-template <typename T>
-inline typename enable_if<is_same<T, const char*>::value, T>::type variantAs(
-    const VariantData* data) {
-  return data != 0 ? data->asString() : 0;
-}
-
-template <typename T>
-T variantAs(VariantData* data, MemoryPool*) {
-  // By default use the read-only conversion.
-  // There are specializations for
-  // - ArrayRef
-  return variantAs<T>(data);
-}
-
-template <typename T>
-inline typename enable_if<is_same<ArrayConstRef, T>::value, T>::type variantAs(
-    const VariantData* data);
-
-template <typename T>
-inline typename enable_if<is_same<ObjectConstRef, T>::value, T>::type variantAs(
-    const VariantData* data);
-
-template <typename T>
-inline typename enable_if<is_same<VariantConstRef, T>::value, T>::type
-variantAs(const VariantData* data);
-
-template <typename T>
-inline typename enable_if<IsWriteableString<T>::value, T>::type variantAs(
-    const VariantData* data);
-
-}  // namespace ARDUINOJSON_NAMESPACE

+ 0 - 57
src/ArduinoJson/Variant/VariantAsImpl.hpp

@@ -1,57 +0,0 @@
-// ArduinoJson - arduinojson.org
-// Copyright Benoit Blanchon 2014-2021
-// MIT License
-
-#pragma once
-
-#include <ArduinoJson/Strings/IsWriteableString.hpp>
-#include <ArduinoJson/Variant/VariantFunctions.hpp>
-#include <ArduinoJson/Variant/VariantRef.hpp>
-
-namespace ARDUINOJSON_NAMESPACE {
-
-template <typename T>
-inline typename enable_if<is_same<ArrayConstRef, T>::value, T>::type variantAs(
-    const VariantData* _data) {
-  return ArrayConstRef(variantAsArray(_data));
-}
-
-template <typename T>
-inline typename enable_if<is_same<ObjectConstRef, T>::value, T>::type variantAs(
-    const VariantData* _data) {
-  return ObjectConstRef(variantAsObject(_data));
-}
-
-template <typename T>
-inline typename enable_if<is_same<VariantConstRef, T>::value, T>::type
-variantAs(const VariantData* _data) {
-  return VariantConstRef(_data);
-}
-
-template <typename T>
-inline typename enable_if<IsWriteableString<T>::value, T>::type variantAs(
-    const VariantData* _data) {
-  const char* cstr = _data != 0 ? _data->asString() : 0;
-  if (cstr)
-    return T(cstr);
-  T s;
-  serializeJson(VariantConstRef(_data), s);
-  return s;
-}
-
-template <>
-inline ArrayRef variantAs<ArrayRef>(VariantData* data, MemoryPool* pool) {
-  return ArrayRef(pool, data != 0 ? data->asArray() : 0);
-}
-
-template <>
-inline ObjectRef variantAs<ObjectRef>(VariantData* data, MemoryPool* pool) {
-  return ObjectRef(pool, data != 0 ? data->asObject() : 0);
-}
-
-template <>
-inline VariantRef variantAs<VariantRef>(VariantData* data, MemoryPool* pool) {
-  return VariantRef(pool, data);
-}
-
-}  // namespace ARDUINOJSON_NAMESPACE

+ 4 - 37
src/ArduinoJson/Variant/VariantFunctions.hpp

@@ -43,34 +43,6 @@ inline bool variantCopyFrom(VariantData *dst, const VariantData *src,
 
 inline int variantCompare(const VariantData *a, const VariantData *b);
 
-inline bool variantSetBoolean(VariantData *var, bool value) {
-  if (!var)
-    return false;
-  var->setBoolean(value);
-  return true;
-}
-
-inline bool variantSetFloat(VariantData *var, Float value) {
-  if (!var)
-    return false;
-  var->setFloat(value);
-  return true;
-}
-
-inline bool variantSetLinkedRaw(VariantData *var,
-                                SerializedValue<const char *> value) {
-  if (!var)
-    return false;
-  var->setLinkedRaw(value);
-  return true;
-}
-
-template <typename T>
-inline bool variantSetOwnedRaw(VariantData *var, SerializedValue<T> value,
-                               MemoryPool *pool) {
-  return var != 0 && var->setOwnedRaw(value, pool);
-}
-
 inline void variantSetNull(VariantData *var) {
   if (!var)
     return;
@@ -85,15 +57,6 @@ inline bool variantSetString(VariantData *var, TAdaptedString value,
   return var->setString(value, pool);
 }
 
-template <typename T>
-inline bool variantSetInteger(VariantData *var, T value) {
-  ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T);
-  if (!var)
-    return false;
-  var->setInteger(value);
-  return true;
-}
-
 inline size_t variantSize(const VariantData *var) {
   return var != 0 ? var->size() : 0;
 }
@@ -134,4 +97,8 @@ NO_INLINE VariantData *variantGetOrAddMember(VariantData *var,
   return var != 0 ? var->getOrAddMember(adaptString(key), pool) : 0;
 }
 
+inline bool variantIsNull(const VariantData *var) {
+  return var == 0 || var->isNull();
+}
+
 }  // namespace ARDUINOJSON_NAMESPACE

+ 0 - 7
src/ArduinoJson/Variant/VariantImpl.hpp

@@ -77,13 +77,6 @@ inline const char *VariantData::asString() const {
   }
 }
 
-template <typename TVariant>
-typename enable_if<IsVisitable<TVariant>::value, bool>::type VariantRef::set(
-    const TVariant &value) const {
-  VariantConstRef v = value;
-  return variantCopyFrom(_data, v._data, _pool);
-}
-
 template <typename T>
 inline typename enable_if<is_same<T, ArrayRef>::value, ArrayRef>::type
 VariantRef::to() const {

+ 0 - 146
src/ArduinoJson/Variant/VariantIs.hpp

@@ -1,146 +0,0 @@
-// ArduinoJson - arduinojson.org
-// Copyright Benoit Blanchon 2014-2021
-// MIT License
-
-#pragma once
-
-#include <ArduinoJson/Polyfills/type_traits.hpp>
-#include <ArduinoJson/Variant/VariantFunctions.hpp>
-
-namespace ARDUINOJSON_NAMESPACE {
-
-inline bool variantIsNull(const VariantData *var) {
-  return var == 0 || var->isNull();
-}
-
-// bool is<char>() const;
-// bool is<signed char>() const;
-// bool is<signed short>() const;
-// bool is<signed int>() const;
-// bool is<signed long>() const;
-// bool is<unsigned char>() const;
-// bool is<unsigned short>() const;
-// bool is<unsigned int>() const;
-// bool is<unsigned long>() const;
-template <typename T>
-NO_INLINE typename enable_if<is_integral<T>::value && !is_same<bool, T>::value,
-                             bool>::type
-variantIs(const VariantData *var) {
-  return var && var->isInteger<T>();
-}
-
-// bool is<double>() const;
-// bool is<float>() const;
-template <typename T>
-NO_INLINE typename enable_if<is_floating_point<T>::value, bool>::type variantIs(
-    const VariantData *var) {
-  return var && var->isFloat();
-}
-
-// bool is<bool>() const
-template <typename T>
-NO_INLINE typename enable_if<is_same<T, bool>::value, bool>::type variantIs(
-    const VariantData *var) {
-  return var && var->isBoolean();
-}
-
-// bool is<const char*>() const;
-// bool is<char*>() const;
-// bool is<std::string>() const;
-// bool is<String>() const;
-template <typename T>
-NO_INLINE typename enable_if<is_same<T, const char *>::value ||
-                                 is_same<T, char *>::value ||
-                                 IsWriteableString<T>::value,
-                             bool>::type
-variantIs(const VariantData *var) {
-  return var && var->isString();
-}
-
-// bool is<ArrayConstRef> const;
-// bool is<const ArrayConstRef> const;
-template <typename T>
-NO_INLINE typename enable_if<
-    is_same<typename remove_const<T>::type, ArrayConstRef>::value, bool>::type
-variantIs(const VariantData *var) {
-  return var && var->isArray();
-}
-
-// bool is<ObjectConstRef> const;
-// bool is<const ObjectConstRef> const;
-template <typename T>
-NO_INLINE typename enable_if<
-    is_same<typename remove_const<T>::type, ObjectConstRef>::value, bool>::type
-variantIs(const VariantData *var) {
-  return var && var->isObject();
-}
-
-// bool is<VariantConstRef> const;
-// bool is<const VariantConstRef> const;
-template <typename T>
-NO_INLINE typename enable_if<
-    is_same<typename remove_const<T>::type, VariantConstRef>::value, bool>::type
-variantIs(const VariantData *var) {
-  return !!var;
-}
-#if ARDUINOJSON_HAS_NULLPTR
-
-// bool is<nullptr_t> const;
-template <typename T>
-NO_INLINE typename enable_if<is_same<T, decltype(nullptr)>::value, bool>::type
-variantIs(const VariantData *var) {
-  return variantIsNull(var);
-}
-#endif
-// bool is<enum>() const;
-template <typename T>
-typename enable_if<is_enum<T>::value, bool>::type variantIs(
-    const VariantData *var) {
-  return variantIs<int>(var);
-}
-
-// bool is<ArrayRef> const;
-// bool is<const ArrayRef> const;
-template <typename T>
-NO_INLINE
-    typename enable_if<is_same<typename remove_const<T>::type, ArrayRef>::value,
-                       bool>::type
-    variantIs(VariantData *var) {
-  return var && var->isArray();
-}
-
-// bool is<ObjectRef> const;
-// bool is<const ObjectRef> const;
-template <typename T>
-NO_INLINE typename enable_if<
-    is_same<typename remove_const<T>::type, ObjectRef>::value, bool>::type
-variantIs(VariantData *var) {
-  return var && var->isObject();
-}
-
-// bool is<VariantRef> const;
-// bool is<const VariantRef> const;
-template <typename T>
-NO_INLINE typename enable_if<
-    is_same<typename remove_const<T>::type, VariantRef>::value, bool>::type
-variantIs(VariantData *var) {
-  return !!var;
-}
-
-// bool is<ArrayRef> const;
-// bool is<const ArrayRef> const;
-// bool is<ObjectRef> const;
-// bool is<const ObjectRef> const;
-// bool is<VariantRef> const;
-// bool is<const VariantRef> const;
-template <typename T>
-typename enable_if<
-    is_same<typename remove_const<T>::type, ArrayRef>::value ||
-        is_same<typename remove_const<T>::type, ObjectRef>::value ||
-        is_same<typename remove_const<T>::type, VariantRef>::value,
-    bool>::type
-variantIs(const VariantData *) {
-  return false;
-}
-
-}  // namespace ARDUINOJSON_NAMESPACE

+ 0 - 1
src/ArduinoJson/Variant/VariantOperators.hpp

@@ -8,7 +8,6 @@
 #include <ArduinoJson/Numbers/arithmeticCompare.hpp>
 #include <ArduinoJson/Polyfills/attributes.hpp>
 #include <ArduinoJson/Polyfills/type_traits.hpp>
-#include <ArduinoJson/Variant/VariantAs.hpp>
 #include <ArduinoJson/Variant/VariantTag.hpp>
 
 namespace ARDUINOJSON_NAMESPACE {

+ 60 - 110
src/ArduinoJson/Variant/VariantRef.hpp

@@ -11,9 +11,8 @@
 #include <ArduinoJson/Misc/Visitable.hpp>
 #include <ArduinoJson/Polyfills/type_traits.hpp>
 #include <ArduinoJson/Strings/StringAdapters.hpp>
-#include <ArduinoJson/Variant/VariantAs.hpp>
+#include <ArduinoJson/Variant/Converter.hpp>
 #include <ArduinoJson/Variant/VariantFunctions.hpp>
-#include <ArduinoJson/Variant/VariantIs.hpp>
 #include <ArduinoJson/Variant/VariantOperators.hpp>
 #include <ArduinoJson/Variant/VariantRef.hpp>
 #include <ArduinoJson/Variant/VariantShortcuts.hpp>
@@ -29,11 +28,6 @@ class ObjectRef;
 template <typename TData>
 class VariantRefBase : public VariantTag {
  public:
-  template <typename T>
-  FORCE_INLINE bool is() const {
-    return variantIs<T>(_data);
-  }
-
   FORCE_INLINE bool isNull() const {
     return variantIsNull(_data);
   }
@@ -57,6 +51,10 @@ class VariantRefBase : public VariantTag {
  protected:
   VariantRefBase(TData *data) : _data(data) {}
   TData *_data;
+
+  friend TData *getData(const VariantRefBase &variant) {
+    return variant._data;
+  }
 };
 
 // A variant that can be a any value serializable to a JSON value.
@@ -85,120 +83,29 @@ class VariantRef : public VariantRefBase<VariantData>,
     return variantSetNull(_data);
   }
 
-  // set(bool value)
-  template <typename T>
-  FORCE_INLINE bool set(
-      T value, typename enable_if<is_same<T, bool>::value>::type * = 0) const {
-    return variantSetBoolean(_data, value);
-  }
-
-  // set(double value);
-  // set(float value);
-  template <typename T>
-  FORCE_INLINE bool set(
-      T value,
-      typename enable_if<is_floating_point<T>::value>::type * = 0) const {
-    return variantSetFloat(_data, static_cast<Float>(value));
-  }
-
-  // set(char)
-  // set(signed short)
-  // set(signed int)
-  // set(signed long)
-  // set(signed char)
-  // set(unsigned short)
-  // set(unsigned int)
-  // set(unsigned long)
   template <typename T>
-  FORCE_INLINE bool set(
-      T value,
-      typename enable_if<is_integral<T>::value && !is_same<bool, T>::value &&
-                         !is_same<char, T>::value>::type * = 0) const {
-    return variantSetInteger<T>(_data, value);
+  FORCE_INLINE bool set(const T &value) const {
+    return Converter<T>::toJson(*this, value);
   }
 
-  // set(SerializedValue<const char *>)
-  FORCE_INLINE bool set(SerializedValue<const char *> value) const {
-    return variantSetLinkedRaw(_data, value);
-  }
-
-  // set(SerializedValue<std::string>)
-  // set(SerializedValue<String>)
-  // set(SerializedValue<const __FlashStringHelper*>)
   template <typename T>
-  FORCE_INLINE bool set(
-      SerializedValue<T> value,
-      typename enable_if<!is_same<const char *, T>::value>::type * = 0) const {
-    return variantSetOwnedRaw(_data, value, _pool);
+  FORCE_INLINE bool set(T *value) const {
+    return Converter<T *>::toJson(*this, value);
   }
 
-  // set(const std::string&)
-  // set(const String&)
-  template <typename T>
-  FORCE_INLINE bool set(
-      const T &value,
-      typename enable_if<IsString<T>::value>::type * = 0) const {
-    return variantSetString(_data, adaptString(value), _pool);
-  }
-  // set(char*)
-  // set(const __FlashStringHelper*)
-  // set(const char*)
   template <typename T>
-  FORCE_INLINE bool set(
-      T *value, typename enable_if<IsString<T *>::value>::type * = 0) const {
-    return variantSetString(_data, adaptString(value), _pool);
-  }
-
-  // set(VariantRef)
-  // set(VariantConstRef)
-  // set(ArrayRef)
-  // set(ArrayConstRef)
-  // set(ObjectRef)
-  // set(ObjecConstRef)
-  // set(const JsonDocument&)
-  template <typename TVariant>
-  typename enable_if<IsVisitable<TVariant>::value, bool>::type set(
-      const TVariant &value) const;
-
-  // set(enum value)
-  template <typename T>
-  FORCE_INLINE bool set(
-      T value, typename enable_if<is_enum<T>::value>::type * = 0) const {
-    return variantSetInteger(_data, static_cast<Integer>(value));
-  }
-
-#if ARDUINOJSON_HAS_NULLPTR
-  // set(nullptr_t)
-  FORCE_INLINE bool set(decltype(nullptr)) const {
-    variantSetNull(_data);
-    return true;
+  FORCE_INLINE T as() const {
+    return Converter<T>::fromJson(*this);
   }
-#endif
 
   template <typename T>
-  FORCE_INLINE T as() const {
-    /********************************************************************
-     **                THIS IS NOT A BUG IN THE LIBRARY                **
-     **                --------------------------------                **
-     **  Get a compilation error pointing here?                        **
-     **  It doesn't mean the error *is* here.                          **
-     **  Often, it's because you try to extract the wrong value type.  **
-     **                                                                **
-     **  For example:                                                  **
-     **    char* name = doc["name"];                                   **
-     **    char age = doc["age"];                                      **
-     **    auto city = doc["city"].as<char*>()                         **
-     **  Instead, use:                                                 **
-     **    const char* name = doc["name"];                             **
-     **    int8_t age = doc["age"];                                    **
-     **    auto city = doc["city"].as<const char*>()                   **
-     ********************************************************************/
-    return variantAs<T>(_data, _pool);
+  FORCE_INLINE bool is() const {
+    return Converter<T>::checkJson(*this);
   }
 
   template <typename T>
   FORCE_INLINE operator T() const {
-    return variantAs<T>(_data, _pool);
+    return Converter<T>::fromJson(*this);
   }
 
   template <typename TVisitor>
@@ -273,7 +180,11 @@ class VariantRef : public VariantRefBase<VariantData>,
 
  private:
   MemoryPool *_pool;
-};  // namespace ARDUINOJSON_NAMESPACE
+
+  friend MemoryPool *getPool(const VariantRef &variant) {
+    return variant._pool;
+  }
+};
 
 class VariantConstRef : public VariantRefBase<const VariantData>,
                         public VariantOperators<VariantConstRef>,
@@ -294,12 +205,17 @@ class VariantConstRef : public VariantRefBase<const VariantData>,
 
   template <typename T>
   FORCE_INLINE T as() const {
-    return variantAs<T>(_data);
+    return Converter<T>::fromJson(*this);
+  }
+
+  template <typename T>
+  FORCE_INLINE bool is() const {
+    return Converter<T>::checkJson(*this);
   }
 
   template <typename T>
   FORCE_INLINE operator T() const {
-    return variantAs<T>(_data);
+    return Converter<T>::fromJson(*this);
   }
 
   FORCE_INLINE VariantConstRef getElement(size_t) const;
@@ -344,4 +260,38 @@ class VariantConstRef : public VariantRefBase<const VariantData>,
     return getMember(key);
   }
 };
+
+template <>
+struct Converter<VariantRef> {
+  static bool toJson(VariantRef variant, VariantRef value) {
+    return variantCopyFrom(getData(variant), getData(value), getPool(variant));
+  }
+  static VariantRef fromJson(VariantRef variant) {
+    return variant;
+  }
+  static bool checkJson(VariantRef variant) {
+    VariantData *data = getData(variant);
+    return !!data;
+  }
+  static bool checkJson(VariantConstRef) {
+    return false;
+  }
+};
+
+template <>
+struct Converter<VariantConstRef> {
+  static bool toJson(VariantRef variant, VariantConstRef value) {
+    return variantCopyFrom(getData(variant), getData(value), getPool(variant));
+  }
+
+  static VariantConstRef fromJson(VariantConstRef variant) {
+    return VariantConstRef(getData(variant));
+  }
+
+  static bool checkJson(VariantConstRef variant) {
+    const VariantData *data = getData(variant);
+    return !!data;
+  }
+};
+
 }  // namespace ARDUINOJSON_NAMESPACE