diff --git a/CHANGELOG.md b/CHANGELOG.md index a4fdd3ba..4e8f7086 100644 --- a/CHANGELOG.md +++ b/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` diff --git a/README.md b/README.md index 5b5206c7..4f4d9704 100644 --- a/README.md +++ b/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 diff --git a/extras/tests/JsonVariant/CMakeLists.txt b/extras/tests/JsonVariant/CMakeLists.txt index a35770a6..3fbe04a9 100644 --- a/extras/tests/JsonVariant/CMakeLists.txt +++ b/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 diff --git a/extras/tests/JsonVariant/converters.cpp b/extras/tests/JsonVariant/converters.cpp new file mode 100644 index 00000000..9493a97a --- /dev/null +++ b/extras/tests/JsonVariant/converters.cpp @@ -0,0 +1,144 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2021 +// MIT License + +#include +#include +#include + +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() && variant["month"].is() && + variant["year"].is(); +} +} // 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() returns true") { + doc["date"]["day"] = 2; + doc["date"]["month"] = 3; + doc["date"]["year"] = 2021; + + REQUIRE(doc["date"].is()); + } + + SECTION("is() returns false") { + doc["date"]["day"] = 2; + doc["date"]["month"] = 3; + doc["date"]["year"] = "2021"; + + REQUIRE(doc["date"].is() == 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 { + 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() && variant["imag"].is(); + } +}; +} // 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() returns true") { + doc["value"]["real"] = 2; + doc["value"]["imag"] = 3; + + REQUIRE(doc["value"].is()); + } + + SECTION("is() returns false") { + doc["value"]["real"] = 2; + doc["value"]["imag"] = "3"; + + REQUIRE(doc["value"].is() == false); + } + + SECTION("convert value to JSON") { + doc["value"] = Complex(19, 3); + + REQUIRE(doc["value"]["real"] == 19); + REQUIRE(doc["value"]["imag"] == 3); + } +} diff --git a/extras/tests/JsonVariant/is.cpp b/extras/tests/JsonVariant/is.cpp index 66ee7c87..8af6c6e5 100644 --- a/extras/tests/JsonVariant/is.cpp +++ b/extras/tests/JsonVariant/is.cpp @@ -19,7 +19,7 @@ TEST_CASE("JsonVariant::is()") { CHECK(variant.is() == false); CHECK(variant.is() == false); CHECK(variant.is() == false); - CHECK(variant.is() == false); + CHECK(variant.is() == false); CHECK(variant.is() == false); CHECK(variant.is() == false); CHECK(variant.is() == false); @@ -32,7 +32,7 @@ TEST_CASE("JsonVariant::is()") { CHECK(variant.is() == false); CHECK(variant.is() == false); CHECK(variant.is() == false); - CHECK(variant.is() == false); + CHECK(variant.is() == false); CHECK(variant.is() == false); CHECK(variant.is() == false); CHECK(variant.is() == false); @@ -47,7 +47,7 @@ TEST_CASE("JsonVariant::is()") { CHECK(variant.is() == true); CHECK(variant.is() == false); CHECK(variant.is() == false); - CHECK(variant.is() == false); + CHECK(variant.is() == false); CHECK(variant.is() == false); CHECK(variant.is() == false); CHECK(variant.is() == false); @@ -62,7 +62,7 @@ TEST_CASE("JsonVariant::is()") { CHECK(variant.is() == true); CHECK(variant.is() == false); CHECK(variant.is() == false); - CHECK(variant.is() == false); + CHECK(variant.is() == false); CHECK(variant.is() == false); CHECK(variant.is() == false); CHECK(variant.is() == false); @@ -83,7 +83,7 @@ TEST_CASE("JsonVariant::is()") { CHECK(variant.is() == false); CHECK(variant.is() == false); CHECK(variant.is() == false); - CHECK(variant.is() == false); + CHECK(variant.is() == false); CHECK(variant.is() == false); } @@ -97,7 +97,7 @@ TEST_CASE("JsonVariant::is()") { CHECK(variant.is() == false); CHECK(variant.is() == false); CHECK(variant.is() == false); - CHECK(variant.is() == false); + CHECK(variant.is() == false); CHECK(variant.is() == false); CHECK(variant.is() == false); CHECK(variant.is() == false); @@ -106,7 +106,7 @@ TEST_CASE("JsonVariant::is()") { SECTION("const char*") { variant.set("4.2"); - CHECK(variant.is() == true); + CHECK(variant.is() == true); CHECK(variant.is() == true); CHECK(variant.is() == true); CHECK(variant.is() == true); @@ -132,7 +132,7 @@ TEST_CASE("JsonVariant::is()") { CHECK(variant.is() == false); CHECK(variant.is() == false); CHECK(variant.is() == false); - CHECK(variant.is() == false); + CHECK(variant.is() == false); CHECK(variant.is() == false); } @@ -148,7 +148,7 @@ TEST_CASE("JsonVariant::is()") { CHECK(variant.is() == false); CHECK(variant.is() == false); CHECK(variant.is() == false); - CHECK(variant.is() == false); + CHECK(variant.is() == false); CHECK(variant.is() == false); CHECK(variant.is() == true); CHECK(variant.is() == true); @@ -170,7 +170,7 @@ TEST_CASE("JsonVariantConst::is()") { CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); - CHECK(cvariant.is() == false); + CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); @@ -183,7 +183,7 @@ TEST_CASE("JsonVariantConst::is()") { CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); - CHECK(cvariant.is() == false); + CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); @@ -198,7 +198,7 @@ TEST_CASE("JsonVariantConst::is()") { CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); - CHECK(cvariant.is() == false); + CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); @@ -213,7 +213,7 @@ TEST_CASE("JsonVariantConst::is()") { CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); - CHECK(cvariant.is() == false); + CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); @@ -234,7 +234,7 @@ TEST_CASE("JsonVariantConst::is()") { CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); - CHECK(cvariant.is() == false); + CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); } @@ -248,7 +248,7 @@ TEST_CASE("JsonVariantConst::is()") { CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); - CHECK(cvariant.is() == false); + CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); @@ -257,7 +257,7 @@ TEST_CASE("JsonVariantConst::is()") { SECTION("const char*") { variant.set("4.2"); - CHECK(cvariant.is() == true); + CHECK(cvariant.is() == true); CHECK(cvariant.is() == true); CHECK(cvariant.is() == true); CHECK(cvariant.is() == false); @@ -282,7 +282,7 @@ TEST_CASE("JsonVariantConst::is()") { CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); - CHECK(cvariant.is() == false); + CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); } @@ -298,7 +298,7 @@ TEST_CASE("JsonVariantConst::is()") { CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); - CHECK(cvariant.is() == false); + CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); } } diff --git a/extras/tests/JsonVariant/undefined.cpp b/extras/tests/JsonVariant/undefined.cpp index 32726510..9f5307af 100644 --- a/extras/tests/JsonVariant/undefined.cpp +++ b/extras/tests/JsonVariant/undefined.cpp @@ -47,8 +47,8 @@ TEST_CASE("JsonVariant undefined") { REQUIRE(variant.is() == false); } - SECTION("char*") { - REQUIRE(variant.is() == false); + SECTION("const char*") { + REQUIRE(variant.is() == false); } SECTION("double") { diff --git a/extras/tests/Misc/Readers.cpp b/extras/tests/Misc/Readers.cpp index 678dc161..b3e92948 100644 --- a/extras/tests/Misc/Readers.cpp +++ b/extras/tests/Misc/Readers.cpp @@ -3,7 +3,7 @@ // MIT License #define ARDUINOJSON_ENABLE_ARDUINO_STREAM 1 -#include +#include #include using namespace ARDUINOJSON_NAMESPACE; diff --git a/extras/tests/Numbers/parseDouble.cpp b/extras/tests/Numbers/parseDouble.cpp index 282ba9f9..3747c08b 100644 --- a/extras/tests/Numbers/parseDouble.cpp +++ b/extras/tests/Numbers/parseDouble.cpp @@ -6,8 +6,7 @@ #define ARDUINOJSON_ENABLE_NAN 1 #define ARDUINOJSON_ENABLE_INFINITY 1 -#include -#include +#include #include using namespace ARDUINOJSON_NAMESPACE; diff --git a/extras/tests/Numbers/parseFloat.cpp b/extras/tests/Numbers/parseFloat.cpp index 60380208..132c942e 100644 --- a/extras/tests/Numbers/parseFloat.cpp +++ b/extras/tests/Numbers/parseFloat.cpp @@ -6,8 +6,7 @@ #define ARDUINOJSON_ENABLE_NAN 1 #define ARDUINOJSON_ENABLE_INFINITY 1 -#include -#include +#include #include using namespace ARDUINOJSON_NAMESPACE; diff --git a/extras/tests/Numbers/parseInteger.cpp b/extras/tests/Numbers/parseInteger.cpp index fb2ed3db..87b2a087 100644 --- a/extras/tests/Numbers/parseInteger.cpp +++ b/extras/tests/Numbers/parseInteger.cpp @@ -3,8 +3,7 @@ // MIT License #include -#include -#include +#include #include using namespace ARDUINOJSON_NAMESPACE; diff --git a/extras/tests/Numbers/parseNumber.cpp b/extras/tests/Numbers/parseNumber.cpp index b8911600..7a5576f9 100644 --- a/extras/tests/Numbers/parseNumber.cpp +++ b/extras/tests/Numbers/parseNumber.cpp @@ -2,9 +2,7 @@ // Copyright Benoit Blanchon 2014-2021 // MIT License -#include -#include -#include +#include #include using namespace ARDUINOJSON_NAMESPACE; diff --git a/src/ArduinoJson.hpp b/src/ArduinoJson.hpp index 52b4f3be..a3bae3c0 100644 --- a/src/ArduinoJson.hpp +++ b/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" diff --git a/src/ArduinoJson/Array/ArrayRef.hpp b/src/ArduinoJson/Array/ArrayRef.hpp index b92107c1..709aebb5 100644 --- a/src/ArduinoJson/Array/ArrayRef.hpp +++ b/src/ArduinoJson/Array/ArrayRef.hpp @@ -164,4 +164,42 @@ class ArrayRef : public ArrayRefBase, private: MemoryPool* _pool; }; + +template <> +struct Converter { + 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 { + 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 diff --git a/src/ArduinoJson/Array/ArrayShortcuts.hpp b/src/ArduinoJson/Array/ArrayShortcuts.hpp index e39043aa..ff9b8588 100644 --- a/src/ArduinoJson/Array/ArrayShortcuts.hpp +++ b/src/ArduinoJson/Array/ArrayShortcuts.hpp @@ -9,6 +9,8 @@ namespace ARDUINOJSON_NAMESPACE { // Forward declarations. +class ArrayRef; +class ObjectRef; template class ElementProxy; diff --git a/src/ArduinoJson/Array/ElementProxy.hpp b/src/ArduinoJson/Array/ElementProxy.hpp index 23ab5194..e2a3095a 100644 --- a/src/ArduinoJson/Array/ElementProxy.hpp +++ b/src/ArduinoJson/Array/ElementProxy.hpp @@ -170,6 +170,10 @@ class ElementProxy : public VariantOperators >, return _array.getOrAddElement(_index); } + friend bool convertToJson(VariantRef variant, const this_type& value) { + return variant.set(value.getUpstreamElement()); + } + TArray _array; const size_t _index; }; diff --git a/src/ArduinoJson/Array/Utilities.hpp b/src/ArduinoJson/Array/Utilities.hpp index 6deba8fe..f0d89a93 100644 --- a/src/ArduinoJson/Array/Utilities.hpp +++ b/src/ArduinoJson/Array/Utilities.hpp @@ -76,7 +76,8 @@ class ArrayCopier1D : public Visitor { VariantSlot* slot = array.head(); while (slot != 0 && size < _capacity) { - _destination[size++] = variantAs(slot->data()); + _destination[size++] = + Converter::fromJson(VariantConstRef(slot->data())); slot = slot->next(); } return size; diff --git a/src/ArduinoJson/Document/JsonDocument.hpp b/src/ArduinoJson/Document/JsonDocument.hpp index 2d4c2af3..ccbf35f0 100644 --- a/src/ArduinoJson/Document/JsonDocument.hpp +++ b/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()); +} + } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Numbers/parseNumber.hpp b/src/ArduinoJson/Numbers/parseNumber.hpp index eb3ce579..c6cd369c 100644 --- a/src/ArduinoJson/Numbers/parseNumber.hpp +++ b/src/ArduinoJson/Numbers/parseNumber.hpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include 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(&value); + return Converter::fromJson(VariantConstRef(&value)); } } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Object/MemberProxy.hpp b/src/ArduinoJson/Object/MemberProxy.hpp index 85e27aec..2661a2d2 100644 --- a/src/ArduinoJson/Object/MemberProxy.hpp +++ b/src/ArduinoJson/Object/MemberProxy.hpp @@ -193,6 +193,10 @@ class MemberProxy : public VariantOperators >, return _object.getOrAddMember(_key); } + friend bool convertToJson(VariantRef variant, const this_type &value) { + return variant.set(value.getUpstreamMember()); + } + TObject _object; TStringRef _key; }; diff --git a/src/ArduinoJson/Object/ObjectRef.hpp b/src/ArduinoJson/Object/ObjectRef.hpp index 618f1ecd..86b885f2 100644 --- a/src/ArduinoJson/Object/ObjectRef.hpp +++ b/src/ArduinoJson/Object/ObjectRef.hpp @@ -236,4 +236,42 @@ class ObjectRef : public ObjectRefBase, private: MemoryPool* _pool; }; + +template <> +struct Converter { + 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 { + 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 diff --git a/src/ArduinoJson/Variant/Converter.hpp b/src/ArduinoJson/Variant/Converter.hpp new file mode 100644 index 00000000..855956c1 --- /dev/null +++ b/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 +struct Converter; + +} // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Variant/ConverterImpl.hpp b/src/ArduinoJson/Variant/ConverterImpl.hpp new file mode 100644 index 00000000..f3bc4498 --- /dev/null +++ b/src/ArduinoJson/Variant/ConverterImpl.hpp @@ -0,0 +1,209 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2021 +// MIT License + +#pragma once + +#include +#include +#include + +namespace ARDUINOJSON_NAMESPACE { + +template +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 +struct Converter< + T, typename enable_if::value && !is_same::value && + !is_same::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(); + } + + static bool checkJson(VariantConstRef variant) { + const VariantData* data = getData(variant); + return data && data->isInteger(); + } +}; + +template +struct Converter::value>::type> { + static bool toJson(VariantRef variant, T value) { + return variant.set(static_cast(value)); + } + + static T fromJson(VariantConstRef variant) { + const VariantData* data = getData(variant); + return data ? static_cast(data->asIntegral()) : T(); + } + + static bool checkJson(VariantConstRef variant) { + const VariantData* data = getData(variant); + return data && data->isInteger(); + } +}; + +template <> +struct Converter { + 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 +struct Converter::value>::type> { + static bool toJson(VariantRef variant, T value) { + VariantData* data = getData(variant); + if (!data) + return false; + data->setFloat(static_cast(value)); + return true; + } + + static T fromJson(VariantConstRef variant) { + const VariantData* data = getData(variant); + return data ? data->asFloat() : false; + } + + static bool checkJson(VariantConstRef variant) { + const VariantData* data = getData(variant); + return data && data->isFloat(); + } +}; + +template <> +struct Converter { + 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 +inline typename enable_if::value, bool>::type convertToJson( + VariantRef variant, const T& value) { + VariantData* data = getData(variant); + MemoryPool* pool = getPool(variant); + return variantSetString(data, adaptString(value), pool); +} + +template +inline typename enable_if::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 +inline typename enable_if::value, bool>::type +canConvertFromJson(T&, VariantConstRef variant) { + const VariantData* data = getData(variant); + return data && data->isString(); +} + +template <> +struct Converter > { + static bool toJson(VariantRef variant, SerializedValue value) { + VariantData* data = getData(variant); + if (!data) + return false; + data->setLinkedRaw(value); + return true; + } +}; + +// SerializedValue +// SerializedValue +// SerializedValue +template +struct Converter, + typename enable_if::value>::type> { + static bool toJson(VariantRef variant, SerializedValue value) { + VariantData* data = getData(variant); + MemoryPool* pool = getPool(variant); + return data != 0 && data->setOwnedRaw(value, pool); + } +}; + +#if ARDUINOJSON_HAS_NULLPTR + +template <> +struct Converter { + 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 diff --git a/src/ArduinoJson/Variant/VariantAs.hpp b/src/ArduinoJson/Variant/VariantAs.hpp deleted file mode 100644 index d7ef8737..00000000 --- a/src/ArduinoJson/Variant/VariantAs.hpp +++ /dev/null @@ -1,76 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2021 -// MIT License - -#pragma once - -#include -#include - -namespace ARDUINOJSON_NAMESPACE { - -class ArrayRef; -class ArrayConstRef; -class ObjectRef; -class ObjectConstRef; -class VariantRef; -class VariantConstRef; - -template -inline typename enable_if::value && !is_same::value && - !is_same::value, - T>::type -variantAs(const VariantData* data) { - ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T); - return data != 0 ? data->asIntegral() : T(0); -} - -template -inline typename enable_if::value, T>::type variantAs( - const VariantData* data) { - return data != 0 ? static_cast(data->asIntegral()) : T(); -} - -template -inline typename enable_if::value, T>::type variantAs( - const VariantData* data) { - return data != 0 ? data->asBoolean() : false; -} - -template -inline typename enable_if::value, T>::type variantAs( - const VariantData* data) { - return data != 0 ? data->asFloat() : T(0); -} - -template -inline typename enable_if::value, T>::type variantAs( - const VariantData* data) { - return data != 0 ? data->asString() : 0; -} - -template -T variantAs(VariantData* data, MemoryPool*) { - // By default use the read-only conversion. - // There are specializations for - // - ArrayRef - return variantAs(data); -} - -template -inline typename enable_if::value, T>::type variantAs( - const VariantData* data); - -template -inline typename enable_if::value, T>::type variantAs( - const VariantData* data); - -template -inline typename enable_if::value, T>::type -variantAs(const VariantData* data); - -template -inline typename enable_if::value, T>::type variantAs( - const VariantData* data); - -} // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Variant/VariantAsImpl.hpp b/src/ArduinoJson/Variant/VariantAsImpl.hpp deleted file mode 100644 index 702d6cd7..00000000 --- a/src/ArduinoJson/Variant/VariantAsImpl.hpp +++ /dev/null @@ -1,57 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2021 -// MIT License - -#pragma once - -#include -#include -#include - -namespace ARDUINOJSON_NAMESPACE { - -template -inline typename enable_if::value, T>::type variantAs( - const VariantData* _data) { - return ArrayConstRef(variantAsArray(_data)); -} - -template -inline typename enable_if::value, T>::type variantAs( - const VariantData* _data) { - return ObjectConstRef(variantAsObject(_data)); -} - -template -inline typename enable_if::value, T>::type -variantAs(const VariantData* _data) { - return VariantConstRef(_data); -} - -template -inline typename enable_if::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(VariantData* data, MemoryPool* pool) { - return ArrayRef(pool, data != 0 ? data->asArray() : 0); -} - -template <> -inline ObjectRef variantAs(VariantData* data, MemoryPool* pool) { - return ObjectRef(pool, data != 0 ? data->asObject() : 0); -} - -template <> -inline VariantRef variantAs(VariantData* data, MemoryPool* pool) { - return VariantRef(pool, data); -} - -} // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Variant/VariantFunctions.hpp b/src/ArduinoJson/Variant/VariantFunctions.hpp index eba84ade..7151cd10 100644 --- a/src/ArduinoJson/Variant/VariantFunctions.hpp +++ b/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 value) { - if (!var) - return false; - var->setLinkedRaw(value); - return true; -} - -template -inline bool variantSetOwnedRaw(VariantData *var, SerializedValue 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 -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 diff --git a/src/ArduinoJson/Variant/VariantImpl.hpp b/src/ArduinoJson/Variant/VariantImpl.hpp index 2d02bc4b..35d4b99c 100644 --- a/src/ArduinoJson/Variant/VariantImpl.hpp +++ b/src/ArduinoJson/Variant/VariantImpl.hpp @@ -77,13 +77,6 @@ inline const char *VariantData::asString() const { } } -template -typename enable_if::value, bool>::type VariantRef::set( - const TVariant &value) const { - VariantConstRef v = value; - return variantCopyFrom(_data, v._data, _pool); -} - template inline typename enable_if::value, ArrayRef>::type VariantRef::to() const { diff --git a/src/ArduinoJson/Variant/VariantIs.hpp b/src/ArduinoJson/Variant/VariantIs.hpp deleted file mode 100644 index 09736fc7..00000000 --- a/src/ArduinoJson/Variant/VariantIs.hpp +++ /dev/null @@ -1,146 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2021 -// MIT License - -#pragma once - -#include -#include - -namespace ARDUINOJSON_NAMESPACE { - -inline bool variantIsNull(const VariantData *var) { - return var == 0 || var->isNull(); -} - -// bool is() const; -// bool is() const; -// bool is() const; -// bool is() const; -// bool is() const; -// bool is() const; -// bool is() const; -// bool is() const; -// bool is() const; -template -NO_INLINE typename enable_if::value && !is_same::value, - bool>::type -variantIs(const VariantData *var) { - return var && var->isInteger(); -} - -// bool is() const; -// bool is() const; -template -NO_INLINE typename enable_if::value, bool>::type variantIs( - const VariantData *var) { - return var && var->isFloat(); -} - -// bool is() const -template -NO_INLINE typename enable_if::value, bool>::type variantIs( - const VariantData *var) { - return var && var->isBoolean(); -} - -// bool is() const; -// bool is() const; -// bool is() const; -// bool is() const; -template -NO_INLINE typename enable_if::value || - is_same::value || - IsWriteableString::value, - bool>::type -variantIs(const VariantData *var) { - return var && var->isString(); -} - -// bool is const; -// bool is const; -template -NO_INLINE typename enable_if< - is_same::type, ArrayConstRef>::value, bool>::type -variantIs(const VariantData *var) { - return var && var->isArray(); -} - -// bool is const; -// bool is const; -template -NO_INLINE typename enable_if< - is_same::type, ObjectConstRef>::value, bool>::type -variantIs(const VariantData *var) { - return var && var->isObject(); -} - -// bool is const; -// bool is const; -template -NO_INLINE typename enable_if< - is_same::type, VariantConstRef>::value, bool>::type -variantIs(const VariantData *var) { - return !!var; -} -#if ARDUINOJSON_HAS_NULLPTR - -// bool is const; -template -NO_INLINE typename enable_if::value, bool>::type -variantIs(const VariantData *var) { - return variantIsNull(var); -} -#endif -// bool is() const; -template -typename enable_if::value, bool>::type variantIs( - const VariantData *var) { - return variantIs(var); -} - -// bool is const; -// bool is const; -template -NO_INLINE - typename enable_if::type, ArrayRef>::value, - bool>::type - variantIs(VariantData *var) { - return var && var->isArray(); -} - -// bool is const; -// bool is const; -template -NO_INLINE typename enable_if< - is_same::type, ObjectRef>::value, bool>::type -variantIs(VariantData *var) { - return var && var->isObject(); -} - -// bool is const; -// bool is const; -template -NO_INLINE typename enable_if< - is_same::type, VariantRef>::value, bool>::type -variantIs(VariantData *var) { - return !!var; -} - -// bool is const; -// bool is const; -// bool is const; -// bool is const; -// bool is const; -// bool is const; -template -typename enable_if< - is_same::type, ArrayRef>::value || - is_same::type, ObjectRef>::value || - is_same::type, VariantRef>::value, - bool>::type -variantIs(const VariantData *) { - return false; -} - -} // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Variant/VariantOperators.hpp b/src/ArduinoJson/Variant/VariantOperators.hpp index 763626ae..2fb4edab 100644 --- a/src/ArduinoJson/Variant/VariantOperators.hpp +++ b/src/ArduinoJson/Variant/VariantOperators.hpp @@ -8,7 +8,6 @@ #include #include #include -#include #include namespace ARDUINOJSON_NAMESPACE { diff --git a/src/ArduinoJson/Variant/VariantRef.hpp b/src/ArduinoJson/Variant/VariantRef.hpp index c17d096a..5f654d02 100644 --- a/src/ArduinoJson/Variant/VariantRef.hpp +++ b/src/ArduinoJson/Variant/VariantRef.hpp @@ -11,9 +11,8 @@ #include #include #include -#include +#include #include -#include #include #include #include @@ -29,11 +28,6 @@ class ObjectRef; template class VariantRefBase : public VariantTag { public: - template - FORCE_INLINE bool is() const { - return variantIs(_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, return variantSetNull(_data); } - // set(bool value) template - FORCE_INLINE bool set( - T value, typename enable_if::value>::type * = 0) const { - return variantSetBoolean(_data, value); + FORCE_INLINE bool set(const T &value) const { + return Converter::toJson(*this, value); } - // set(double value); - // set(float value); template - FORCE_INLINE bool set( - T value, - typename enable_if::value>::type * = 0) const { - return variantSetFloat(_data, static_cast(value)); + FORCE_INLINE bool set(T *value) const { + return Converter::toJson(*this, 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 - FORCE_INLINE bool set( - T value, - typename enable_if::value && !is_same::value && - !is_same::value>::type * = 0) const { - return variantSetInteger(_data, value); - } - - // set(SerializedValue) - FORCE_INLINE bool set(SerializedValue value) const { - return variantSetLinkedRaw(_data, value); - } - - // set(SerializedValue) - // set(SerializedValue) - // set(SerializedValue) - template - FORCE_INLINE bool set( - SerializedValue value, - typename enable_if::value>::type * = 0) const { - return variantSetOwnedRaw(_data, value, _pool); - } - - // set(const std::string&) - // set(const String&) - template - FORCE_INLINE bool set( - const T &value, - typename enable_if::value>::type * = 0) const { - return variantSetString(_data, adaptString(value), _pool); - } - // set(char*) - // set(const __FlashStringHelper*) - // set(const char*) - template - FORCE_INLINE bool set( - T *value, typename enable_if::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 enable_if::value, bool>::type set( - const TVariant &value) const; - - // set(enum value) - template - FORCE_INLINE bool set( - T value, typename enable_if::value>::type * = 0) const { - return variantSetInteger(_data, static_cast(value)); - } - -#if ARDUINOJSON_HAS_NULLPTR - // set(nullptr_t) - FORCE_INLINE bool set(decltype(nullptr)) const { - variantSetNull(_data); - return true; - } -#endif - template 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() ** - ** Instead, use: ** - ** const char* name = doc["name"]; ** - ** int8_t age = doc["age"]; ** - ** auto city = doc["city"].as() ** - ********************************************************************/ - return variantAs(_data, _pool); + return Converter::fromJson(*this); + } + + template + FORCE_INLINE bool is() const { + return Converter::checkJson(*this); } template FORCE_INLINE operator T() const { - return variantAs(_data, _pool); + return Converter::fromJson(*this); } template @@ -273,7 +180,11 @@ class VariantRef : public VariantRefBase, private: MemoryPool *_pool; -}; // namespace ARDUINOJSON_NAMESPACE + + friend MemoryPool *getPool(const VariantRef &variant) { + return variant._pool; + } +}; class VariantConstRef : public VariantRefBase, public VariantOperators, @@ -294,12 +205,17 @@ class VariantConstRef : public VariantRefBase, template FORCE_INLINE T as() const { - return variantAs(_data); + return Converter::fromJson(*this); + } + + template + FORCE_INLINE bool is() const { + return Converter::checkJson(*this); } template FORCE_INLINE operator T() const { - return variantAs(_data); + return Converter::fromJson(*this); } FORCE_INLINE VariantConstRef getElement(size_t) const; @@ -344,4 +260,38 @@ class VariantConstRef : public VariantRefBase, return getMember(key); } }; + +template <> +struct Converter { + 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 { + 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