forked from bblanchon/ArduinoJson
Simplified JsonVariant::as<T>()
to always return T
This commit is contained in:
31
CHANGELOG.md
31
CHANGELOG.md
@ -11,9 +11,12 @@ HEAD
|
||||
* Added `JsonVariant::is<JsonArrayConst/JsonObjectConst>()` (issue #1412)
|
||||
* Added `JsonVariant::is<JsonVariant/JsonVariantConst>()` (issue #1412)
|
||||
* Changed `JsonVariantConst::is<JsonArray/JsonObject>()` to return `false` (issue #1412)
|
||||
* Simplified `JsonVariant::as<T>()` to always return `T` (see below)
|
||||
|
||||
> ### BREAKING CHANGES
|
||||
>
|
||||
> #### Support for `char` removed
|
||||
>
|
||||
> We cannot cast a `JsonVariant` to a `char` anymore, so the following will break:
|
||||
> ```c++
|
||||
> char age = doc["age"]; // error: no matching function for call to 'variantAs(VariantData*&)'
|
||||
@ -34,13 +37,39 @@ HEAD
|
||||
> doc["age"] = age; // OK
|
||||
> ```
|
||||
>
|
||||
> #### `as<T>()` always returns `T`
|
||||
>
|
||||
> Previously, `JsonVariant::as<T>()` could return a type different from `T`.
|
||||
> The most common example is `as<char*>()` that returned a `const char*`.
|
||||
> While this feature simplified a few use cases, it was confusing and complicated the
|
||||
> implementation of custom converters.
|
||||
>
|
||||
> Starting from this version, `as<T>` doesn't try to auto-correct the return type and always return `T`,
|
||||
> which means that you cannot write this anymore:
|
||||
>
|
||||
> ```c++
|
||||
> Serial.println(doc["sensor"].as<char*>()); // error: invalid conversion from 'const char*' to 'char*' [-fpermissive]
|
||||
|
||||
> ```
|
||||
>
|
||||
> Instead, you must write:
|
||||
>
|
||||
> ```c++
|
||||
> Serial.println(doc["sensor"].as<const char*>()); // OK
|
||||
> ```
|
||||
>
|
||||
>
|
||||
> #### `DeserializationError::NotSupported` removed
|
||||
>
|
||||
> On a different topic, `DeserializationError::NotSupported` has been removed.
|
||||
> Instead of returning this error:
|
||||
>
|
||||
> * `deserializeJson()` leaves `\uXXXX` unchanged (only when `ARDUINOJSON_DECODE_UNICODE` is `0`)
|
||||
> * `deserializeMsgPack()` replaces unsupported values with `null`s
|
||||
>
|
||||
> Lastly, a very minor change conserns `JsonVariantConst::is<T>()`.
|
||||
> #### Const-aware `is<T>()`
|
||||
>
|
||||
> Lastly, a very minor change concerns `JsonVariantConst::is<T>()`.
|
||||
> It used to return `true` for `JsonArray` and `JsonOject`, but now it returns `false`.
|
||||
> Instead, you must use `JsonArrayConst` and `JsonObjectConst`.
|
||||
|
||||
|
@ -92,7 +92,7 @@ void setup() {
|
||||
|
||||
// Extract values
|
||||
Serial.println(F("Response:"));
|
||||
Serial.println(doc["sensor"].as<char*>());
|
||||
Serial.println(doc["sensor"].as<const char*>());
|
||||
Serial.println(doc["time"].as<long>());
|
||||
Serial.println(doc["data"][0].as<float>(), 6);
|
||||
Serial.println(doc["data"][1].as<float>(), 6);
|
||||
|
@ -60,7 +60,6 @@ TEST_CASE("JsonArray::operator[]") {
|
||||
|
||||
array[0] = str;
|
||||
REQUIRE(str == array[0].as<const char*>());
|
||||
REQUIRE(str == array[0].as<char*>()); // <- short hand
|
||||
REQUIRE(true == array[0].is<const char*>());
|
||||
REQUIRE(false == array[0].is<int>());
|
||||
}
|
||||
|
@ -99,8 +99,8 @@ TEST_CASE("deserialize JSON array") {
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(2 == arr.size());
|
||||
REQUIRE(arr[0].as<char*>() == 0);
|
||||
REQUIRE(arr[1].as<char*>() == 0);
|
||||
REQUIRE(arr[0].as<const char*>() == 0);
|
||||
REQUIRE(arr[1].as<const char*>() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -182,8 +182,8 @@ TEST_CASE("deserialize JSON object") {
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 2);
|
||||
REQUIRE(obj["key1"].as<char*>() == 0);
|
||||
REQUIRE(obj["key2"].as<char*>() == 0);
|
||||
REQUIRE(obj["key1"].as<const char*>() == 0);
|
||||
REQUIRE(obj["key2"].as<const char*>() == 0);
|
||||
}
|
||||
|
||||
SECTION("Array") {
|
||||
|
@ -48,7 +48,6 @@ TEST_CASE("JsonObject::operator[]") {
|
||||
REQUIRE(true == obj["hello"].is<const char*>());
|
||||
REQUIRE(false == obj["hello"].is<long>());
|
||||
REQUIRE(std::string("h3110") == obj["hello"].as<const char*>());
|
||||
REQUIRE(std::string("h3110") == obj["hello"].as<char*>()); // <- short hand
|
||||
}
|
||||
|
||||
SECTION("array") {
|
||||
@ -189,7 +188,7 @@ TEST_CASE("JsonObject::operator[]") {
|
||||
|
||||
obj["hello"] = vla;
|
||||
|
||||
REQUIRE(std::string("world") == obj["hello"].as<char*>());
|
||||
REQUIRE(std::string("world") == obj["hello"].as<const char*>());
|
||||
}
|
||||
|
||||
SECTION("obj.set(VLA, str)") {
|
||||
@ -209,7 +208,7 @@ TEST_CASE("JsonObject::operator[]") {
|
||||
|
||||
obj["hello"].set(vla);
|
||||
|
||||
REQUIRE(std::string("world") == obj["hello"].as<char*>());
|
||||
REQUIRE(std::string("world") == obj["hello"].as<const char*>());
|
||||
}
|
||||
|
||||
SECTION("obj[VLA]") {
|
||||
|
@ -22,7 +22,7 @@ TEST_CASE("JsonVariant::as()") {
|
||||
REQUIRE(false == variant.as<bool>());
|
||||
REQUIRE(0 == variant.as<int>());
|
||||
REQUIRE(0.0f == variant.as<float>());
|
||||
REQUIRE(0 == variant.as<char*>());
|
||||
REQUIRE(0 == variant.as<const char*>());
|
||||
REQUIRE("null" == variant.as<std::string>());
|
||||
}
|
||||
|
||||
@ -104,7 +104,7 @@ TEST_CASE("JsonVariant::as()") {
|
||||
REQUIRE(variant.as<bool>() == true);
|
||||
REQUIRE(variant.as<long>() == 0L);
|
||||
REQUIRE(variant.as<const char*>() == std::string("hello"));
|
||||
REQUIRE(variant.as<char*>() == std::string("hello"));
|
||||
REQUIRE(variant.as<const char*>() == std::string("hello"));
|
||||
REQUIRE(variant.as<std::string>() == std::string("hello"));
|
||||
}
|
||||
|
||||
@ -114,7 +114,7 @@ TEST_CASE("JsonVariant::as()") {
|
||||
REQUIRE(variant.as<bool>() == true);
|
||||
REQUIRE(variant.as<long>() == 4L);
|
||||
REQUIRE(variant.as<double>() == 4.2);
|
||||
REQUIRE(variant.as<char*>() == std::string("4.2"));
|
||||
REQUIRE(variant.as<const char*>() == std::string("4.2"));
|
||||
REQUIRE(variant.as<std::string>() == std::string("4.2"));
|
||||
}
|
||||
|
||||
@ -211,8 +211,7 @@ TEST_CASE("JsonVariant::as()") {
|
||||
REQUIRE(cvar.as<bool>() == true);
|
||||
REQUIRE(cvar.as<long>() == 0L);
|
||||
REQUIRE(cvar.as<const char*>() == std::string("hello"));
|
||||
REQUIRE(cvar.as<char*>() == std::string("hello"));
|
||||
// REQUIRE(cvar.as<std::string>() == std::string("hello"));
|
||||
REQUIRE(cvar.as<std::string>() == std::string("hello"));
|
||||
}
|
||||
|
||||
SECTION("as<enum>()") {
|
||||
|
@ -17,8 +17,8 @@ TEST_CASE("JsonVariant undefined") {
|
||||
REQUIRE(variant.as<unsigned>() == 0);
|
||||
}
|
||||
|
||||
SECTION("char*") {
|
||||
REQUIRE(variant.as<char*>() == 0);
|
||||
SECTION("const char*") {
|
||||
REQUIRE(variant.as<const char*>() == 0);
|
||||
}
|
||||
|
||||
SECTION("double") {
|
||||
|
@ -19,7 +19,7 @@ TEST_CASE("ARDUINOJSON_ENABLE_STRING_DEDUPLICATION = 0") {
|
||||
deserializeJson(doc, "[\"example\",\"example\"]");
|
||||
|
||||
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 16);
|
||||
CHECK(doc[0].as<char*>() != doc[1].as<char*>());
|
||||
CHECK(doc[0].as<const char*>() != doc[1].as<const char*>());
|
||||
}
|
||||
|
||||
SECTION("Deduplicate keys") {
|
||||
@ -42,7 +42,7 @@ TEST_CASE("ARDUINOJSON_ENABLE_STRING_DEDUPLICATION = 0") {
|
||||
doc.add(std::string("example"));
|
||||
|
||||
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 16);
|
||||
CHECK(doc[0].as<char*>() != doc[1].as<char*>());
|
||||
CHECK(doc[0].as<const char*>() != doc[1].as<const char*>());
|
||||
}
|
||||
|
||||
SECTION("char*") {
|
||||
@ -51,7 +51,7 @@ TEST_CASE("ARDUINOJSON_ENABLE_STRING_DEDUPLICATION = 0") {
|
||||
doc.add(value);
|
||||
|
||||
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 16);
|
||||
CHECK(doc[0].as<char*>() != doc[1].as<char*>());
|
||||
CHECK(doc[0].as<const char*>() != doc[1].as<const char*>());
|
||||
}
|
||||
|
||||
SECTION("Arduino String") {
|
||||
@ -59,7 +59,7 @@ TEST_CASE("ARDUINOJSON_ENABLE_STRING_DEDUPLICATION = 0") {
|
||||
doc.add(String("example"));
|
||||
|
||||
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 16);
|
||||
CHECK(doc[0].as<char*>() != doc[1].as<char*>());
|
||||
CHECK(doc[0].as<const char*>() != doc[1].as<const char*>());
|
||||
}
|
||||
|
||||
SECTION("Flash string") {
|
||||
@ -67,7 +67,7 @@ TEST_CASE("ARDUINOJSON_ENABLE_STRING_DEDUPLICATION = 0") {
|
||||
doc.add(F("example"));
|
||||
|
||||
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 16);
|
||||
CHECK(doc[0].as<char*>() != doc[1].as<char*>());
|
||||
CHECK(doc[0].as<const char*>() != doc[1].as<const char*>());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ TEST_CASE("ARDUINOJSON_ENABLE_STRING_DEDUPLICATION = 1") {
|
||||
deserializeJson(doc, "[\"example\",\"example\"]");
|
||||
|
||||
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
|
||||
CHECK(doc[0].as<char*>() == doc[1].as<char*>());
|
||||
CHECK(doc[0].as<const char*>() == doc[1].as<const char*>());
|
||||
}
|
||||
|
||||
SECTION("Deduplicate keys") {
|
||||
@ -41,7 +41,7 @@ TEST_CASE("ARDUINOJSON_ENABLE_STRING_DEDUPLICATION = 1") {
|
||||
doc.add(std::string("example"));
|
||||
|
||||
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
|
||||
CHECK(doc[0].as<char*>() == doc[1].as<char*>());
|
||||
CHECK(doc[0].as<const char*>() == doc[1].as<const char*>());
|
||||
}
|
||||
|
||||
SECTION("char*") {
|
||||
@ -50,7 +50,7 @@ TEST_CASE("ARDUINOJSON_ENABLE_STRING_DEDUPLICATION = 1") {
|
||||
doc.add(value);
|
||||
|
||||
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
|
||||
CHECK(doc[0].as<char*>() == doc[1].as<char*>());
|
||||
CHECK(doc[0].as<const char*>() == doc[1].as<const char*>());
|
||||
}
|
||||
|
||||
SECTION("Arduino String") {
|
||||
@ -58,7 +58,7 @@ TEST_CASE("ARDUINOJSON_ENABLE_STRING_DEDUPLICATION = 1") {
|
||||
doc.add(String("example"));
|
||||
|
||||
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
|
||||
CHECK(doc[0].as<char*>() == doc[1].as<char*>());
|
||||
CHECK(doc[0].as<const char*>() == doc[1].as<const char*>());
|
||||
}
|
||||
|
||||
SECTION("Flash string") {
|
||||
@ -66,7 +66,7 @@ TEST_CASE("ARDUINOJSON_ENABLE_STRING_DEDUPLICATION = 1") {
|
||||
doc.add(F("example"));
|
||||
|
||||
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
|
||||
CHECK(doc[0].as<char*>() == doc[1].as<char*>());
|
||||
CHECK(doc[0].as<const char*>() == doc[1].as<const char*>());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,7 +65,7 @@ class ElementProxy : public VariantOperators<ElementProxy<TArray> >,
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
FORCE_INLINE typename VariantAs<T>::type as() const {
|
||||
FORCE_INLINE T as() const {
|
||||
return getUpstreamElement().template as<T>();
|
||||
}
|
||||
|
||||
|
@ -21,12 +21,12 @@ class JsonDocument : public Visitable {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename VariantAs<T>::type as() {
|
||||
T as() {
|
||||
return getVariant().template as<T>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename VariantConstAs<T>::type as() const {
|
||||
T as() const {
|
||||
return getVariant().template as<T>();
|
||||
}
|
||||
|
||||
@ -70,7 +70,7 @@ class JsonDocument : public Visitable {
|
||||
}
|
||||
|
||||
bool set(const JsonDocument& src) {
|
||||
return to<VariantRef>().set(src.as<VariantRef>());
|
||||
return to<VariantRef>().set(src.as<VariantConstRef>());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
@ -82,7 +82,7 @@ class MemberProxy : public VariantOperators<MemberProxy<TObject, TStringRef> >,
|
||||
}
|
||||
|
||||
template <typename TValue>
|
||||
FORCE_INLINE typename VariantAs<TValue>::type as() const {
|
||||
FORCE_INLINE TValue as() const {
|
||||
return getUpstreamMember().template as<TValue>();
|
||||
}
|
||||
|
||||
|
@ -16,42 +16,6 @@ class ObjectConstRef;
|
||||
class VariantRef;
|
||||
class VariantConstRef;
|
||||
|
||||
// A metafunction that returns the type of the value returned by
|
||||
// VariantRef::as<T>()
|
||||
template <typename T>
|
||||
struct VariantAs {
|
||||
typedef T type;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct VariantAs<char*> {
|
||||
typedef const char* type;
|
||||
};
|
||||
|
||||
// A metafunction that returns the type of the value returned by
|
||||
// VariantRef::as<T>()
|
||||
template <typename T>
|
||||
struct VariantConstAs {
|
||||
typedef typename VariantAs<T>::type type;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct VariantConstAs<VariantRef> {
|
||||
typedef VariantConstRef type;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct VariantConstAs<ObjectRef> {
|
||||
typedef ObjectConstRef type;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct VariantConstAs<ArrayRef> {
|
||||
typedef ArrayConstRef type;
|
||||
};
|
||||
|
||||
// ---
|
||||
|
||||
template <typename T>
|
||||
inline typename enable_if<is_integral<T>::value && !is_same<bool, T>::value &&
|
||||
!is_same<char, T>::value,
|
||||
@ -80,10 +44,8 @@ inline typename enable_if<is_floating_point<T>::value, T>::type variantAs(
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline typename enable_if<is_same<T, const char*>::value ||
|
||||
is_same<T, char*>::value,
|
||||
const char*>::type
|
||||
variantAs(const VariantData* data) {
|
||||
inline typename enable_if<is_same<T, const char*>::value, T>::type variantAs(
|
||||
const VariantData* data) {
|
||||
return data != 0 ? data->asString() : 0;
|
||||
}
|
||||
|
||||
|
@ -23,18 +23,25 @@ struct VariantOperators {
|
||||
// int operator|(JsonVariant, int)
|
||||
// float operator|(JsonVariant, float)
|
||||
// bool operator|(JsonVariant, bool)
|
||||
// const char* operator|(JsonVariant, const char*)
|
||||
// char* operator|(JsonVariant, const char*)
|
||||
template <typename T>
|
||||
friend typename enable_if<!IsVariant<T>::value,
|
||||
typename VariantAs<T>::type>::type
|
||||
operator|(const TVariant &variant, T defaultValue) {
|
||||
friend
|
||||
typename enable_if<!IsVariant<T>::value && !is_array<T>::value, T>::type
|
||||
operator|(const TVariant &variant, const T &defaultValue) {
|
||||
if (variant.template is<T>())
|
||||
return variant.template as<T>();
|
||||
else
|
||||
return defaultValue;
|
||||
}
|
||||
//
|
||||
// const char* operator|(JsonVariant, const char*)
|
||||
friend const char *operator|(const TVariant &variant,
|
||||
const char *defaultValue) {
|
||||
if (variant.template is<const char *>())
|
||||
return variant.template as<const char *>();
|
||||
else
|
||||
return defaultValue;
|
||||
}
|
||||
//
|
||||
// JsonVariant operator|(JsonVariant, JsonVariant)
|
||||
template <typename T>
|
||||
friend typename enable_if<IsVariant<T>::value, typename T::variant_type>::type
|
||||
|
@ -176,7 +176,7 @@ class VariantRef : public VariantRefBase<VariantData>,
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
FORCE_INLINE typename VariantAs<T>::type as() const {
|
||||
FORCE_INLINE T as() const {
|
||||
/********************************************************************
|
||||
** THIS IS NOT A BUG IN THE LIBRARY **
|
||||
** -------------------------------- **
|
||||
@ -187,11 +187,13 @@ class VariantRef : public VariantRefBase<VariantData>,
|
||||
** 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<typename VariantAs<T>::type>(_data, _pool);
|
||||
return variantAs<T>(_data, _pool);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@ -291,8 +293,8 @@ class VariantConstRef : public VariantRefBase<const VariantData>,
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
FORCE_INLINE typename VariantConstAs<T>::type as() const {
|
||||
return variantAs<typename VariantConstAs<T>::type>(_data);
|
||||
FORCE_INLINE T as() const {
|
||||
return variantAs<T>(_data);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
Reference in New Issue
Block a user