diff --git a/CHANGELOG.md b/CHANGELOG.md index 53c8f010..a4fdd3ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,9 +11,12 @@ HEAD * Added `JsonVariant::is()` (issue #1412) * Added `JsonVariant::is()` (issue #1412) * Changed `JsonVariantConst::is()` to return `false` (issue #1412) +* Simplified `JsonVariant::as()` 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()` always returns `T` +> +> Previously, `JsonVariant::as()` could return a type different from `T`. +> The most common example is `as()` 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` 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()); // error: invalid conversion from 'const char*' to 'char*' [-fpermissive] + +> ``` +> +> Instead, you must write: +> +> ```c++ +> Serial.println(doc["sensor"].as()); // 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()`. +> #### Const-aware `is()` +> +> Lastly, a very minor change concerns `JsonVariantConst::is()`. > It used to return `true` for `JsonArray` and `JsonOject`, but now it returns `false`. > Instead, you must use `JsonArrayConst` and `JsonObjectConst`. diff --git a/examples/JsonHttpClient/JsonHttpClient.ino b/examples/JsonHttpClient/JsonHttpClient.ino index ff110963..21f0ab73 100644 --- a/examples/JsonHttpClient/JsonHttpClient.ino +++ b/examples/JsonHttpClient/JsonHttpClient.ino @@ -92,7 +92,7 @@ void setup() { // Extract values Serial.println(F("Response:")); - Serial.println(doc["sensor"].as()); + Serial.println(doc["sensor"].as()); Serial.println(doc["time"].as()); Serial.println(doc["data"][0].as(), 6); Serial.println(doc["data"][1].as(), 6); diff --git a/extras/tests/JsonArray/subscript.cpp b/extras/tests/JsonArray/subscript.cpp index e103d014..ddf92aa5 100644 --- a/extras/tests/JsonArray/subscript.cpp +++ b/extras/tests/JsonArray/subscript.cpp @@ -60,7 +60,6 @@ TEST_CASE("JsonArray::operator[]") { array[0] = str; REQUIRE(str == array[0].as()); - REQUIRE(str == array[0].as()); // <- short hand REQUIRE(true == array[0].is()); REQUIRE(false == array[0].is()); } diff --git a/extras/tests/JsonDeserializer/array.cpp b/extras/tests/JsonDeserializer/array.cpp index e0b08a40..ab78060f 100644 --- a/extras/tests/JsonDeserializer/array.cpp +++ b/extras/tests/JsonDeserializer/array.cpp @@ -99,8 +99,8 @@ TEST_CASE("deserialize JSON array") { REQUIRE(err == DeserializationError::Ok); REQUIRE(2 == arr.size()); - REQUIRE(arr[0].as() == 0); - REQUIRE(arr[1].as() == 0); + REQUIRE(arr[0].as() == 0); + REQUIRE(arr[1].as() == 0); } } diff --git a/extras/tests/JsonDeserializer/object.cpp b/extras/tests/JsonDeserializer/object.cpp index 3a95f78f..bd06c723 100644 --- a/extras/tests/JsonDeserializer/object.cpp +++ b/extras/tests/JsonDeserializer/object.cpp @@ -182,8 +182,8 @@ TEST_CASE("deserialize JSON object") { REQUIRE(err == DeserializationError::Ok); REQUIRE(doc.is()); REQUIRE(obj.size() == 2); - REQUIRE(obj["key1"].as() == 0); - REQUIRE(obj["key2"].as() == 0); + REQUIRE(obj["key1"].as() == 0); + REQUIRE(obj["key2"].as() == 0); } SECTION("Array") { diff --git a/extras/tests/JsonObject/subscript.cpp b/extras/tests/JsonObject/subscript.cpp index a0c548b6..55645b9e 100644 --- a/extras/tests/JsonObject/subscript.cpp +++ b/extras/tests/JsonObject/subscript.cpp @@ -48,7 +48,6 @@ TEST_CASE("JsonObject::operator[]") { REQUIRE(true == obj["hello"].is()); REQUIRE(false == obj["hello"].is()); REQUIRE(std::string("h3110") == obj["hello"].as()); - REQUIRE(std::string("h3110") == obj["hello"].as()); // <- short hand } SECTION("array") { @@ -189,7 +188,7 @@ TEST_CASE("JsonObject::operator[]") { obj["hello"] = vla; - REQUIRE(std::string("world") == obj["hello"].as()); + REQUIRE(std::string("world") == obj["hello"].as()); } SECTION("obj.set(VLA, str)") { @@ -209,7 +208,7 @@ TEST_CASE("JsonObject::operator[]") { obj["hello"].set(vla); - REQUIRE(std::string("world") == obj["hello"].as()); + REQUIRE(std::string("world") == obj["hello"].as()); } SECTION("obj[VLA]") { diff --git a/extras/tests/JsonVariant/as.cpp b/extras/tests/JsonVariant/as.cpp index f0419117..a90069ee 100644 --- a/extras/tests/JsonVariant/as.cpp +++ b/extras/tests/JsonVariant/as.cpp @@ -22,7 +22,7 @@ TEST_CASE("JsonVariant::as()") { REQUIRE(false == variant.as()); REQUIRE(0 == variant.as()); REQUIRE(0.0f == variant.as()); - REQUIRE(0 == variant.as()); + REQUIRE(0 == variant.as()); REQUIRE("null" == variant.as()); } @@ -104,7 +104,7 @@ TEST_CASE("JsonVariant::as()") { REQUIRE(variant.as() == true); REQUIRE(variant.as() == 0L); REQUIRE(variant.as() == std::string("hello")); - REQUIRE(variant.as() == std::string("hello")); + REQUIRE(variant.as() == std::string("hello")); REQUIRE(variant.as() == std::string("hello")); } @@ -114,7 +114,7 @@ TEST_CASE("JsonVariant::as()") { REQUIRE(variant.as() == true); REQUIRE(variant.as() == 4L); REQUIRE(variant.as() == 4.2); - REQUIRE(variant.as() == std::string("4.2")); + REQUIRE(variant.as() == std::string("4.2")); REQUIRE(variant.as() == std::string("4.2")); } @@ -211,8 +211,7 @@ TEST_CASE("JsonVariant::as()") { REQUIRE(cvar.as() == true); REQUIRE(cvar.as() == 0L); REQUIRE(cvar.as() == std::string("hello")); - REQUIRE(cvar.as() == std::string("hello")); - // REQUIRE(cvar.as() == std::string("hello")); + REQUIRE(cvar.as() == std::string("hello")); } SECTION("as()") { diff --git a/extras/tests/JsonVariant/undefined.cpp b/extras/tests/JsonVariant/undefined.cpp index 4216b4d7..32726510 100644 --- a/extras/tests/JsonVariant/undefined.cpp +++ b/extras/tests/JsonVariant/undefined.cpp @@ -17,8 +17,8 @@ TEST_CASE("JsonVariant undefined") { REQUIRE(variant.as() == 0); } - SECTION("char*") { - REQUIRE(variant.as() == 0); + SECTION("const char*") { + REQUIRE(variant.as() == 0); } SECTION("double") { diff --git a/extras/tests/MixedConfiguration/enable_string_deduplication_0.cpp b/extras/tests/MixedConfiguration/enable_string_deduplication_0.cpp index a63c2cad..7f086204 100644 --- a/extras/tests/MixedConfiguration/enable_string_deduplication_0.cpp +++ b/extras/tests/MixedConfiguration/enable_string_deduplication_0.cpp @@ -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() != doc[1].as()); + CHECK(doc[0].as() != doc[1].as()); } 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() != doc[1].as()); + CHECK(doc[0].as() != doc[1].as()); } 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() != doc[1].as()); + CHECK(doc[0].as() != doc[1].as()); } 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() != doc[1].as()); + CHECK(doc[0].as() != doc[1].as()); } 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() != doc[1].as()); + CHECK(doc[0].as() != doc[1].as()); } } diff --git a/extras/tests/MixedConfiguration/enable_string_deduplication_1.cpp b/extras/tests/MixedConfiguration/enable_string_deduplication_1.cpp index 0988914e..a2b595ff 100644 --- a/extras/tests/MixedConfiguration/enable_string_deduplication_1.cpp +++ b/extras/tests/MixedConfiguration/enable_string_deduplication_1.cpp @@ -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() == doc[1].as()); + CHECK(doc[0].as() == doc[1].as()); } 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() == doc[1].as()); + CHECK(doc[0].as() == doc[1].as()); } 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() == doc[1].as()); + CHECK(doc[0].as() == doc[1].as()); } 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() == doc[1].as()); + CHECK(doc[0].as() == doc[1].as()); } 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() == doc[1].as()); + CHECK(doc[0].as() == doc[1].as()); } } diff --git a/src/ArduinoJson/Array/ElementProxy.hpp b/src/ArduinoJson/Array/ElementProxy.hpp index 6e19d01c..23ab5194 100644 --- a/src/ArduinoJson/Array/ElementProxy.hpp +++ b/src/ArduinoJson/Array/ElementProxy.hpp @@ -65,7 +65,7 @@ class ElementProxy : public VariantOperators >, } template - FORCE_INLINE typename VariantAs::type as() const { + FORCE_INLINE T as() const { return getUpstreamElement().template as(); } diff --git a/src/ArduinoJson/Document/JsonDocument.hpp b/src/ArduinoJson/Document/JsonDocument.hpp index 6ac1ea2d..2d4c2af3 100644 --- a/src/ArduinoJson/Document/JsonDocument.hpp +++ b/src/ArduinoJson/Document/JsonDocument.hpp @@ -21,12 +21,12 @@ class JsonDocument : public Visitable { } template - typename VariantAs::type as() { + T as() { return getVariant().template as(); } template - typename VariantConstAs::type as() const { + T as() const { return getVariant().template as(); } @@ -70,7 +70,7 @@ class JsonDocument : public Visitable { } bool set(const JsonDocument& src) { - return to().set(src.as()); + return to().set(src.as()); } template diff --git a/src/ArduinoJson/Object/MemberProxy.hpp b/src/ArduinoJson/Object/MemberProxy.hpp index c3cd9d51..85e27aec 100644 --- a/src/ArduinoJson/Object/MemberProxy.hpp +++ b/src/ArduinoJson/Object/MemberProxy.hpp @@ -82,7 +82,7 @@ class MemberProxy : public VariantOperators >, } template - FORCE_INLINE typename VariantAs::type as() const { + FORCE_INLINE TValue as() const { return getUpstreamMember().template as(); } diff --git a/src/ArduinoJson/Variant/VariantAs.hpp b/src/ArduinoJson/Variant/VariantAs.hpp index f011cbd1..d7ef8737 100644 --- a/src/ArduinoJson/Variant/VariantAs.hpp +++ b/src/ArduinoJson/Variant/VariantAs.hpp @@ -16,42 +16,6 @@ class ObjectConstRef; class VariantRef; class VariantConstRef; -// A metafunction that returns the type of the value returned by -// VariantRef::as() -template -struct VariantAs { - typedef T type; -}; - -template <> -struct VariantAs { - typedef const char* type; -}; - -// A metafunction that returns the type of the value returned by -// VariantRef::as() -template -struct VariantConstAs { - typedef typename VariantAs::type type; -}; - -template <> -struct VariantConstAs { - typedef VariantConstRef type; -}; - -template <> -struct VariantConstAs { - typedef ObjectConstRef type; -}; - -template <> -struct VariantConstAs { - typedef ArrayConstRef type; -}; - -// --- - template inline typename enable_if::value && !is_same::value && !is_same::value, @@ -80,10 +44,8 @@ inline typename enable_if::value, T>::type variantAs( } template -inline typename enable_if::value || - is_same::value, - const char*>::type -variantAs(const VariantData* data) { +inline typename enable_if::value, T>::type variantAs( + const VariantData* data) { return data != 0 ? data->asString() : 0; } diff --git a/src/ArduinoJson/Variant/VariantOperators.hpp b/src/ArduinoJson/Variant/VariantOperators.hpp index 87ede585..763626ae 100644 --- a/src/ArduinoJson/Variant/VariantOperators.hpp +++ b/src/ArduinoJson/Variant/VariantOperators.hpp @@ -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 - friend typename enable_if::value, - typename VariantAs::type>::type - operator|(const TVariant &variant, T defaultValue) { + friend + typename enable_if::value && !is_array::value, T>::type + operator|(const TVariant &variant, const T &defaultValue) { if (variant.template is()) return variant.template as(); else return defaultValue; } // + // const char* operator|(JsonVariant, const char*) + friend const char *operator|(const TVariant &variant, + const char *defaultValue) { + if (variant.template is()) + return variant.template as(); + else + return defaultValue; + } + // // JsonVariant operator|(JsonVariant, JsonVariant) template friend typename enable_if::value, typename T::variant_type>::type diff --git a/src/ArduinoJson/Variant/VariantRef.hpp b/src/ArduinoJson/Variant/VariantRef.hpp index 14ed4d10..c17d096a 100644 --- a/src/ArduinoJson/Variant/VariantRef.hpp +++ b/src/ArduinoJson/Variant/VariantRef.hpp @@ -176,7 +176,7 @@ class VariantRef : public VariantRefBase, #endif template - FORCE_INLINE typename VariantAs::type as() const { + FORCE_INLINE T as() const { /******************************************************************** ** THIS IS NOT A BUG IN THE LIBRARY ** ** -------------------------------- ** @@ -187,11 +187,13 @@ class VariantRef : public VariantRefBase, ** 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::type>(_data, _pool); + return variantAs(_data, _pool); } template @@ -291,8 +293,8 @@ class VariantConstRef : public VariantRefBase, } template - FORCE_INLINE typename VariantConstAs::type as() const { - return variantAs::type>(_data); + FORCE_INLINE T as() const { + return variantAs(_data); } template