mirror of
				https://github.com/eledio-devices/thirdparty-ArduinoJson.git
				synced 2025-10-31 00:32:37 +01:00 
			
		
		
		
	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