Added support for custom converters (closes #687)

This commit is contained in:
Benoit Blanchon
2021-03-20 14:52:47 +01:00
parent 53d6f0d492
commit d7f5b56ca4
29 changed files with 554 additions and 470 deletions

View File

@ -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`

View File

@ -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

View File

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

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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") {

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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"

View File

@ -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

View File

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

View File

@ -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;
};

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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;
};

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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 {

View File

@ -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);
FORCE_INLINE bool set(const T &value) const {
return Converter<T>::toJson(*this, 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));
FORCE_INLINE bool set(T *value) const {
return Converter<T *>::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 <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);
}
// 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);
}
// 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;
}
#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);
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, _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