diff --git a/CHANGELOG.md b/CHANGELOG.md index bd72d66d..3d7bbd87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ ArduinoJson: change log ======================= +HEAD +---- + +* Added implicit conversion from `JsonArray` and `JsonObject` to `JsonVariant` + v6.4.0-beta (2018-09-11) ----------- diff --git a/src/ArduinoJson/Data/JsonVariantData.hpp b/src/ArduinoJson/Data/JsonVariantData.hpp index cd4c9d0e..7a148f62 100644 --- a/src/ArduinoJson/Data/JsonVariantData.hpp +++ b/src/ArduinoJson/Data/JsonVariantData.hpp @@ -4,158 +4,32 @@ #pragma once -#include "../Numbers/parseFloat.hpp" -#include "../Numbers/parseInteger.hpp" #include "JsonVariantContent.hpp" #include "JsonVariantType.hpp" namespace ArduinoJson { namespace Internals { +// this struct must be a POD type to prevent error calling offsetof on clang struct JsonVariantData { JsonVariantType type; JsonVariantContent content; - - JsonVariantData() { - type = JSON_NULL; - } - - void setBoolean(bool value) { - type = JSON_BOOLEAN; - content.asInteger = static_cast(value); - } - - void setFloat(JsonFloat value) { - type = JSON_FLOAT; - content.asFloat = value; - } - - void setNegativeInteger(JsonUInt value) { - type = JSON_NEGATIVE_INTEGER; - content.asInteger = value; - } - - void setPostiveInteger(JsonUInt value) { - type = JSON_POSITIVE_INTEGER; - content.asInteger = value; - } - - void setOwnedString(const char *value) { - type = JSON_OWNED_STRING; - content.asString = value; - } - - void setLinkedString(const char *value) { - type = JSON_LINKED_STRING; - content.asString = value; - } - - void setOwnedRaw(const char *data, size_t size) { - type = JSON_OWNED_RAW; - content.asRaw.data = data; - content.asRaw.size = size; - } - - void setLinkedRaw(const char *data, size_t size) { - type = JSON_LINKED_RAW; - content.asRaw.data = data; - content.asRaw.size = size; - } - - void setNull() { - type = JSON_NULL; - } - - JsonArrayData *toArray() { - type = JSON_ARRAY; - content.asArray.head = 0; - content.asArray.tail = 0; - return &content.asArray; - } - - JsonObjectData *toObject() { - type = JSON_OBJECT; - content.asObject.head = 0; - content.asObject.tail = 0; - return &content.asObject; - } - - JsonArrayData *asArray() { - return type == JSON_ARRAY ? &content.asArray : 0; - } - - JsonObjectData *asObject() { - return type == JSON_OBJECT ? &content.asObject : 0; - } - - template - T asInteger() const { - switch (type) { - case JSON_POSITIVE_INTEGER: - case JSON_BOOLEAN: - return T(content.asInteger); - case JSON_NEGATIVE_INTEGER: - return T(~content.asInteger + 1); - case JSON_LINKED_STRING: - case JSON_OWNED_STRING: - return parseInteger(content.asString); - case JSON_FLOAT: - return T(content.asFloat); - default: - return 0; - } - } - - template - T asFloat() const { - switch (type) { - case JSON_POSITIVE_INTEGER: - case JSON_BOOLEAN: - return static_cast(content.asInteger); - case JSON_NEGATIVE_INTEGER: - return -static_cast(content.asInteger); - case JSON_LINKED_STRING: - case JSON_OWNED_STRING: - return parseFloat(content.asString); - case JSON_FLOAT: - return static_cast(content.asFloat); - default: - return 0; - } - } - - const char *asString() const { - return isString() ? content.asString : NULL; - } - - bool isArray() const { - return type == JSON_ARRAY; - } - - bool isBoolean() const { - return type == JSON_BOOLEAN; - } - - bool isFloat() const { - return type == JSON_FLOAT || type == JSON_POSITIVE_INTEGER || - type == JSON_NEGATIVE_INTEGER; - } - - bool isInteger() const { - return type == JSON_POSITIVE_INTEGER || type == JSON_NEGATIVE_INTEGER; - } - - bool isNull() const { - return type == JSON_NULL; - } - - bool isObject() const { - return type == JSON_OBJECT; - } - - bool isString() const { - return type == JSON_LINKED_STRING || type == JSON_OWNED_STRING; - } }; + +inline JsonVariantData *getVariantData(JsonArrayData *arr) { + const ptrdiff_t offset = offsetof(JsonVariantData, content) - + offsetof(JsonVariantContent, asArray); + if (!arr) return 0; + return reinterpret_cast(reinterpret_cast(arr) - + offset); +} + +inline JsonVariantData *getVariantData(JsonObjectData *obj) { + const ptrdiff_t offset = offsetof(JsonVariantData, content) - + offsetof(JsonVariantContent, asObject); + if (!obj) return 0; + return reinterpret_cast(reinterpret_cast(obj) - + offset); +} } // namespace Internals } // namespace ArduinoJson diff --git a/src/ArduinoJson/DynamicJsonDocument.hpp b/src/ArduinoJson/DynamicJsonDocument.hpp index 096604ee..92547e78 100644 --- a/src/ArduinoJson/DynamicJsonDocument.hpp +++ b/src/ArduinoJson/DynamicJsonDocument.hpp @@ -37,7 +37,7 @@ class DynamicJsonDocument { void clear() { _memoryPool.clear(); - _rootData.setNull(); + _rootData.type = Internals::JSON_NULL; } size_t memoryUsage() const { diff --git a/src/ArduinoJson/JsonArray.hpp b/src/ArduinoJson/JsonArray.hpp index 781d801d..3e03a08f 100644 --- a/src/ArduinoJson/JsonArray.hpp +++ b/src/ArduinoJson/JsonArray.hpp @@ -26,9 +26,14 @@ class JsonArray { typedef JsonArrayIterator iterator; FORCE_INLINE JsonArray() : _memoryPool(0), _data(0) {} - FORCE_INLINE JsonArray(Internals::MemoryPool* buf, + FORCE_INLINE JsonArray(Internals::MemoryPool* pool, Internals::JsonArrayData* arr) - : _memoryPool(buf), _data(arr) {} + : _memoryPool(pool), _data(arr) {} + + operator JsonVariant() { + using namespace Internals; + return JsonVariant(_memoryPool, getVariantData(_data)); + } // Adds the specified value at the end of the array. // diff --git a/src/ArduinoJson/JsonObject.hpp b/src/ArduinoJson/JsonObject.hpp index 4fdddbe8..4e67d699 100644 --- a/src/ArduinoJson/JsonObject.hpp +++ b/src/ArduinoJson/JsonObject.hpp @@ -24,6 +24,11 @@ class JsonObject { Internals::JsonObjectData* object) : _memoryPool(buf), _data(object) {} + operator JsonVariant() { + using namespace Internals; + return JsonVariant(_memoryPool, getVariantData(_data)); + } + FORCE_INLINE iterator begin() const { if (!_data) return iterator(); return iterator(_memoryPool, _data->head); diff --git a/src/ArduinoJson/JsonVariant.hpp b/src/ArduinoJson/JsonVariant.hpp index 30f7e56e..7b28f37c 100644 --- a/src/ArduinoJson/JsonVariant.hpp +++ b/src/ArduinoJson/JsonVariant.hpp @@ -11,6 +11,8 @@ #include "JsonVariant.hpp" #include "JsonVariantBase.hpp" #include "Memory/MemoryPool.hpp" +#include "Numbers/parseFloat.hpp" +#include "Numbers/parseInteger.hpp" #include "Polyfills/type_traits.hpp" #include "Serialization/DynamicStringWriter.hpp" #include "SerializedValue.hpp" @@ -41,7 +43,8 @@ class JsonVariant : public Internals::JsonVariantBase { // set(bool value) FORCE_INLINE bool set(bool value) { if (!_data) return false; - _data->setBoolean(value); + _data->type = Internals::JSON_BOOLEAN; + _data->content.asInteger = static_cast(value); return true; } @@ -52,7 +55,8 @@ class JsonVariant : public Internals::JsonVariantBase { T value, typename Internals::enable_if< Internals::is_floating_point::value>::type * = 0) { if (!_data) return false; - _data->setFloat(static_cast(value)); + _data->type = Internals::JSON_FLOAT; + _data->content.asFloat = static_cast(value); return true; } @@ -68,10 +72,13 @@ class JsonVariant : public Internals::JsonVariantBase { Internals::is_signed::value>::type * = 0) { if (!_data) return false; - if (value >= 0) - _data->setPostiveInteger(static_cast(value)); - else - _data->setNegativeInteger(~static_cast(value) + 1); + if (value >= 0) { + _data->type = Internals::JSON_POSITIVE_INTEGER; + _data->content.asInteger = static_cast(value); + } else { + _data->type = Internals::JSON_NEGATIVE_INTEGER; + _data->content.asInteger = ~static_cast(value) + 1; + } return true; } @@ -85,14 +92,17 @@ class JsonVariant : public Internals::JsonVariantBase { Internals::is_unsigned::value>::type * = 0) { if (!_data) return false; - _data->setPostiveInteger(static_cast(value)); + _data->type = Internals::JSON_POSITIVE_INTEGER; + _data->content.asInteger = static_cast(value); return true; } // set(SerializedValue) FORCE_INLINE bool set(Internals::SerializedValue value) { if (!_data) return false; - _data->setLinkedRaw(value.data(), value.size()); + _data->type = Internals::JSON_LINKED_RAW; + _data->content.asRaw.data = value.data(); + _data->content.asRaw.size = value.size(); return true; } @@ -107,11 +117,15 @@ class JsonVariant : public Internals::JsonVariantBase { if (!_data) return false; const char *dup = Internals::makeString(value.data(), value.size()).save(_memoryPool); - if (dup) - _data->setOwnedRaw(dup, value.size()); - else - _data->setNull(); - return true; + if (dup) { + _data->type = Internals::JSON_OWNED_RAW; + _data->content.asRaw.data = dup; + _data->content.asRaw.size = value.size(); + return true; + } else { + _data->type = Internals::JSON_NULL; + return false; + } } // set(const std::string&) @@ -124,10 +138,11 @@ class JsonVariant : public Internals::JsonVariantBase { if (!_data) return false; const char *dup = Internals::makeString(value).save(_memoryPool); if (dup) { - _data->setOwnedString(dup); + _data->type = Internals::JSON_OWNED_STRING; + _data->content.asString = dup; return true; } else { - _data->setNull(); + _data->type = Internals::JSON_NULL; return false; } } @@ -141,10 +156,11 @@ class JsonVariant : public Internals::JsonVariantBase { if (!_data) return false; const char *dup = Internals::makeString(value).save(_memoryPool); if (dup) { - _data->setOwnedString(dup); + _data->type = Internals::JSON_OWNED_STRING; + _data->content.asString = dup; return true; } else { - _data->setNull(); + _data->type = Internals::JSON_NULL; return false; } } @@ -152,7 +168,8 @@ class JsonVariant : public Internals::JsonVariantBase { // set(const char*); FORCE_INLINE bool set(const char *value) { if (!_data) return false; - _data->setLinkedString(value); + _data->type = Internals::JSON_LINKED_STRING; + _data->content.asString = value; return true; } @@ -179,14 +196,28 @@ class JsonVariant : public Internals::JsonVariantBase { FORCE_INLINE const typename Internals::enable_if< Internals::is_integral::value, T>::type as() const { - return _data ? _data->asInteger() : T(); + if (!_data) return 0; + switch (_data->type) { + case Internals::JSON_POSITIVE_INTEGER: + case Internals::JSON_BOOLEAN: + return T(_data->content.asInteger); + case Internals::JSON_NEGATIVE_INTEGER: + return T(~_data->content.asInteger + 1); + case Internals::JSON_LINKED_STRING: + case Internals::JSON_OWNED_STRING: + return Internals::parseInteger(_data->content.asString); + case Internals::JSON_FLOAT: + return T(_data->content.asFloat); + default: + return 0; + } } // bool as() const template FORCE_INLINE const typename Internals::enable_if< Internals::is_same::value, T>::type as() const { - return _data && _data->asInteger() != 0; + return as() != 0; } // // double as() const; @@ -195,7 +226,21 @@ class JsonVariant : public Internals::JsonVariantBase { FORCE_INLINE const typename Internals::enable_if< Internals::is_floating_point::value, T>::type as() const { - return _data ? _data->asFloat() : 0; + if (!_data) return 0; + switch (_data->type) { + case Internals::JSON_POSITIVE_INTEGER: + case Internals::JSON_BOOLEAN: + return static_cast(_data->content.asInteger); + case Internals::JSON_NEGATIVE_INTEGER: + return -static_cast(_data->content.asInteger); + case Internals::JSON_LINKED_STRING: + case Internals::JSON_OWNED_STRING: + return Internals::parseFloat(_data->content.asString); + case Internals::JSON_FLOAT: + return static_cast(_data->content.asFloat); + default: + return 0; + } } // // const char* as() const; @@ -206,7 +251,12 @@ class JsonVariant : public Internals::JsonVariantBase { Internals::is_same::value, const char *>::type as() const { - return _data ? _data->asString() : 0; + if (!_data) return 0; + if (_data && (_data->type == Internals::JSON_LINKED_STRING || + _data->type == Internals::JSON_OWNED_STRING)) + return _data->content.asString; + else + return 0; } // // std::string as() const; @@ -216,7 +266,7 @@ class JsonVariant : public Internals::JsonVariantBase { typename Internals::enable_if::value, T>::type as() const { - const char *cstr = _data ? _data->asString() : 0; + const char *cstr = as(); if (cstr) return T(cstr); T s; serializeJson(*this, s); @@ -266,7 +316,8 @@ class JsonVariant : public Internals::JsonVariantBase { FORCE_INLINE typename Internals::enable_if::value, bool>::type is() const { - return _data && _data->isInteger(); + return _data && (_data->type == Internals::JSON_POSITIVE_INTEGER || + _data->type == Internals::JSON_NEGATIVE_INTEGER); } // // bool is() const; @@ -276,7 +327,9 @@ class JsonVariant : public Internals::JsonVariantBase { typename Internals::enable_if::value, bool>::type is() const { - return _data && _data->isFloat(); + return _data && (_data->type == Internals::JSON_FLOAT || + _data->type == Internals::JSON_POSITIVE_INTEGER || + _data->type == Internals::JSON_NEGATIVE_INTEGER); } // // bool is() const @@ -284,7 +337,7 @@ class JsonVariant : public Internals::JsonVariantBase { FORCE_INLINE typename Internals::enable_if::value, bool>::type is() const { - return _data && _data->isBoolean(); + return _data && _data->type == Internals::JSON_BOOLEAN; } // // bool is() const; @@ -295,7 +348,8 @@ class JsonVariant : public Internals::JsonVariantBase { Internals::is_same::value, bool>::type is() const { - return _data && _data->isString(); + return _data && (_data->type == Internals::JSON_LINKED_STRING || + _data->type == Internals::JSON_OWNED_STRING); } // // bool is const; @@ -306,7 +360,7 @@ class JsonVariant : public Internals::JsonVariantBase { JsonArray>::value, bool>::type is() const { - return _data && _data->isArray(); + return _data && _data->type == Internals::JSON_ARRAY; } // // bool is const; @@ -317,11 +371,11 @@ class JsonVariant : public Internals::JsonVariantBase { JsonObject>::value, bool>::type is() const { - return _data && _data->isObject(); + return _data && _data->type == Internals::JSON_OBJECT; } FORCE_INLINE bool isNull() const { - return _data == 0 || _data->isNull(); + return _data == 0 || _data->type == Internals::JSON_NULL; } FORCE_INLINE bool isInvalid() const { @@ -354,5 +408,16 @@ class JsonVariant : public Internals::JsonVariantBase { private: Internals::MemoryPool *_memoryPool; Internals::JsonVariantData *_data; -}; // namespace ArduinoJson +}; + +class JsonVariantLocal : public JsonVariant { + public: + explicit JsonVariantLocal(Internals::MemoryPool *memoryPool) + : JsonVariant(memoryPool, &_localData) { + _localData.type = Internals::JSON_NULL; + } + + private: + Internals::JsonVariantData _localData; +}; } // namespace ArduinoJson diff --git a/src/ArduinoJson/JsonVariantImpl.hpp b/src/ArduinoJson/JsonVariantImpl.hpp index 42dfebd9..d6322dbf 100644 --- a/src/ArduinoJson/JsonVariantImpl.hpp +++ b/src/ArduinoJson/JsonVariantImpl.hpp @@ -34,7 +34,7 @@ inline bool JsonVariant::set( inline bool JsonVariant::set(const JsonVariant& value) { if (!_data) return false; if (!value._data) { - _data->setNull(); + _data->type = Internals::JSON_NULL; return true; } switch (value._data->type) { @@ -59,7 +59,10 @@ inline typename Internals::enable_if< JsonArray>::value, JsonArray>::type JsonVariant::as() const { - return _data ? JsonArray(_memoryPool, _data->asArray()) : JsonArray(); + if (_data && _data->type == Internals::JSON_ARRAY) + return JsonArray(_memoryPool, &_data->content.asArray); + else + return JsonArray(); } template @@ -68,7 +71,10 @@ inline typename Internals::enable_if< JsonObject>::value, T>::type JsonVariant::as() const { - return _data ? JsonObject(_memoryPool, _data->asObject()) : JsonObject(); + if (_data && _data->type == Internals::JSON_OBJECT) + return JsonObject(_memoryPool, &_data->content.asObject); + else + return JsonObject(); } template @@ -76,7 +82,10 @@ inline typename Internals::enable_if::value, JsonArray>::type JsonVariant::to() { if (!_data) return JsonArray(); - return JsonArray(_memoryPool, _data->toArray()); + _data->type = Internals::JSON_ARRAY; + _data->content.asArray.head = 0; + _data->content.asArray.tail = 0; + return JsonArray(_memoryPool, &_data->content.asArray); } template @@ -84,7 +93,10 @@ typename Internals::enable_if::value, JsonObject>::type JsonVariant::to() { if (!_data) return JsonObject(); - return JsonObject(_memoryPool, _data->toObject()); + _data->type = Internals::JSON_OBJECT; + _data->content.asObject.head = 0; + _data->content.asObject.tail = 0; + return JsonObject(_memoryPool, &_data->content.asObject); } template @@ -92,7 +104,7 @@ typename Internals::enable_if::value, JsonVariant>::type JsonVariant::to() { if (!_data) return JsonVariant(); - _data->setNull(); + _data->type = Internals::JSON_NULL; return *this; } diff --git a/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp b/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp index c1cd3182..fb35e06c 100644 --- a/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp +++ b/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp @@ -277,13 +277,12 @@ class MsgPackDeserializer { if (_nestingLimit == 0) return DeserializationError::TooDeep; --_nestingLimit; for (; n; --n) { - JsonVariantData keyData; - JsonVariant key(_memoryPool, &keyData); + JsonVariantLocal key(_memoryPool); DeserializationError err = parse(key); if (err) return err; - if (!keyData.isString()) return DeserializationError::NotSupported; + if (!key.is()) return DeserializationError::NotSupported; - JsonVariant value = object.set(keyData.asString()); + JsonVariant value = object.set(key.as()); if (value.isInvalid()) return DeserializationError::NoMemory; err = parse(value); diff --git a/src/ArduinoJson/StaticJsonDocument.hpp b/src/ArduinoJson/StaticJsonDocument.hpp index 6e2af146..352d3497 100644 --- a/src/ArduinoJson/StaticJsonDocument.hpp +++ b/src/ArduinoJson/StaticJsonDocument.hpp @@ -39,7 +39,7 @@ class StaticJsonDocument { void clear() { _memoryPool.clear(); - _rootData.setNull(); + _rootData.type = Internals::JSON_NULL; } size_t memoryUsage() const { diff --git a/test/JsonVariant/CMakeLists.txt b/test/JsonVariant/CMakeLists.txt index 9004b3be..84f6f722 100644 --- a/test/JsonVariant/CMakeLists.txt +++ b/test/JsonVariant/CMakeLists.txt @@ -8,6 +8,7 @@ add_executable(JsonVariantTests copy.cpp is.cpp isnull.cpp + misc.cpp or.cpp set_get.cpp subscript.cpp diff --git a/test/JsonVariant/misc.cpp b/test/JsonVariant/misc.cpp new file mode 100644 index 00000000..68077a5f --- /dev/null +++ b/test/JsonVariant/misc.cpp @@ -0,0 +1,50 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +TEST_CASE("JsonVariant from JsonArray") { + SECTION("JsonArray is null") { + JsonArray arr; + JsonVariant v = arr; + REQUIRE(v.isNull() == true); + } + + SECTION("JsonArray is not null") { + DynamicJsonDocument doc; + JsonArray arr = doc.to(); + arr.add(12); + arr.add(34); + + JsonVariant v = arr; + + REQUIRE(v.is() == true); + REQUIRE(v.size() == 2); + REQUIRE(v[0] == 12); + REQUIRE(v[1] == 34); + } +} + +TEST_CASE("JsonVariant from JsonObject") { + SECTION("JsonObject is null") { + JsonObject obj; + JsonVariant v = obj; + REQUIRE(v.isNull() == true); + } + + SECTION("JsonObject is not null") { + DynamicJsonDocument doc; + JsonObject obj = doc.to(); + obj["a"] = 12; + obj["b"] = 34; + + JsonVariant v = obj; + + REQUIRE(v.is() == true); + REQUIRE(v.size() == 2); + REQUIRE(v["a"] == 12); + REQUIRE(v["b"] == 34); + } +} diff --git a/test/JsonVariant/set_get.cpp b/test/JsonVariant/set_get.cpp index 9ab526f5..05982079 100644 --- a/test/JsonVariant/set_get.cpp +++ b/test/JsonVariant/set_get.cpp @@ -205,7 +205,20 @@ TEST_CASE("JsonVariant and strings") { REQUIRE(variant == "hello"); } - - // TODO: string - // TODO: serialized() +} + +TEST_CASE("JsonVariant with not enough memory") { + StaticJsonDocument<1> doc; + + JsonVariant v = doc.to(); + + SECTION("std::string") { + v.set(std::string("hello")); + REQUIRE(v.isNull()); + } + + SECTION("Serialized") { + v.set(serialized(std::string("hello"))); + REQUIRE(v.isNull()); + } }