Added implicit conversion from JsonArray and JsonObject to JsonVariant

This commit is contained in:
Benoit Blanchon
2018-10-01 12:55:40 +02:00
parent 2d54019f83
commit 29e71cbb16
12 changed files with 220 additions and 191 deletions

View File

@ -1,6 +1,11 @@
ArduinoJson: change log
=======================
HEAD
----
* Added implicit conversion from `JsonArray` and `JsonObject` to `JsonVariant`
v6.4.0-beta (2018-09-11)
-----------

View File

@ -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<JsonUInt>(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 <typename T>
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<T>(content.asString);
case JSON_FLOAT:
return T(content.asFloat);
default:
return 0;
}
}
template <typename T>
T asFloat() const {
switch (type) {
case JSON_POSITIVE_INTEGER:
case JSON_BOOLEAN:
return static_cast<T>(content.asInteger);
case JSON_NEGATIVE_INTEGER:
return -static_cast<T>(content.asInteger);
case JSON_LINKED_STRING:
case JSON_OWNED_STRING:
return parseFloat<T>(content.asString);
case JSON_FLOAT:
return static_cast<T>(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<JsonVariantData *>(reinterpret_cast<char *>(arr) -
offset);
}
inline JsonVariantData *getVariantData(JsonObjectData *obj) {
const ptrdiff_t offset = offsetof(JsonVariantData, content) -
offsetof(JsonVariantContent, asObject);
if (!obj) return 0;
return reinterpret_cast<JsonVariantData *>(reinterpret_cast<char *>(obj) -
offset);
}
} // namespace Internals
} // namespace ArduinoJson

View File

@ -37,7 +37,7 @@ class DynamicJsonDocument {
void clear() {
_memoryPool.clear();
_rootData.setNull();
_rootData.type = Internals::JSON_NULL;
}
size_t memoryUsage() const {

View File

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

View File

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

View File

@ -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<JsonVariant> {
// 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<Internals::JsonUInt>(value);
return true;
}
@ -52,7 +55,8 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
T value, typename Internals::enable_if<
Internals::is_floating_point<T>::value>::type * = 0) {
if (!_data) return false;
_data->setFloat(static_cast<Internals::JsonFloat>(value));
_data->type = Internals::JSON_FLOAT;
_data->content.asFloat = static_cast<Internals::JsonFloat>(value);
return true;
}
@ -68,10 +72,13 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
Internals::is_signed<T>::value>::type * =
0) {
if (!_data) return false;
if (value >= 0)
_data->setPostiveInteger(static_cast<Internals::JsonUInt>(value));
else
_data->setNegativeInteger(~static_cast<Internals::JsonUInt>(value) + 1);
if (value >= 0) {
_data->type = Internals::JSON_POSITIVE_INTEGER;
_data->content.asInteger = static_cast<Internals::JsonUInt>(value);
} else {
_data->type = Internals::JSON_NEGATIVE_INTEGER;
_data->content.asInteger = ~static_cast<Internals::JsonUInt>(value) + 1;
}
return true;
}
@ -85,14 +92,17 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
Internals::is_unsigned<T>::value>::type * =
0) {
if (!_data) return false;
_data->setPostiveInteger(static_cast<Internals::JsonUInt>(value));
_data->type = Internals::JSON_POSITIVE_INTEGER;
_data->content.asInteger = static_cast<Internals::JsonUInt>(value);
return true;
}
// set(SerializedValue<const char *>)
FORCE_INLINE bool set(Internals::SerializedValue<const char *> 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<JsonVariant> {
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<JsonVariant> {
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<JsonVariant> {
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<JsonVariant> {
// 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<JsonVariant> {
FORCE_INLINE const typename Internals::enable_if<
Internals::is_integral<T>::value, T>::type
as() const {
return _data ? _data->asInteger<T>() : 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<T>(_data->content.asString);
case Internals::JSON_FLOAT:
return T(_data->content.asFloat);
default:
return 0;
}
}
// bool as<bool>() const
template <typename T>
FORCE_INLINE const typename Internals::enable_if<
Internals::is_same<T, bool>::value, T>::type
as() const {
return _data && _data->asInteger<int>() != 0;
return as<int>() != 0;
}
//
// double as<double>() const;
@ -195,7 +226,21 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
FORCE_INLINE const typename Internals::enable_if<
Internals::is_floating_point<T>::value, T>::type
as() const {
return _data ? _data->asFloat<T>() : 0;
if (!_data) return 0;
switch (_data->type) {
case Internals::JSON_POSITIVE_INTEGER:
case Internals::JSON_BOOLEAN:
return static_cast<T>(_data->content.asInteger);
case Internals::JSON_NEGATIVE_INTEGER:
return -static_cast<T>(_data->content.asInteger);
case Internals::JSON_LINKED_STRING:
case Internals::JSON_OWNED_STRING:
return Internals::parseFloat<T>(_data->content.asString);
case Internals::JSON_FLOAT:
return static_cast<T>(_data->content.asFloat);
default:
return 0;
}
}
//
// const char* as<const char*>() const;
@ -206,7 +251,12 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
Internals::is_same<T, char *>::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<std::string>() const;
@ -216,7 +266,7 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
typename Internals::enable_if<Internals::IsWriteableString<T>::value,
T>::type
as() const {
const char *cstr = _data ? _data->asString() : 0;
const char *cstr = as<const char *>();
if (cstr) return T(cstr);
T s;
serializeJson(*this, s);
@ -266,7 +316,8 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
FORCE_INLINE typename Internals::enable_if<Internals::is_integral<T>::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<double>() const;
@ -276,7 +327,9 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
typename Internals::enable_if<Internals::is_floating_point<T>::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<bool>() const
@ -284,7 +337,7 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
FORCE_INLINE typename Internals::enable_if<Internals::is_same<T, bool>::value,
bool>::type
is() const {
return _data && _data->isBoolean();
return _data && _data->type == Internals::JSON_BOOLEAN;
}
//
// bool is<const char*>() const;
@ -295,7 +348,8 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
Internals::is_same<T, char *>::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<JsonArray> const;
@ -306,7 +360,7 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
JsonArray>::value,
bool>::type
is() const {
return _data && _data->isArray();
return _data && _data->type == Internals::JSON_ARRAY;
}
//
// bool is<JsonObject> const;
@ -317,11 +371,11 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
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<JsonVariant> {
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

View File

@ -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 <typename T>
@ -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 <typename T>
@ -76,7 +82,10 @@ inline typename Internals::enable_if<Internals::is_same<T, JsonArray>::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 <typename T>
@ -84,7 +93,10 @@ typename Internals::enable_if<Internals::is_same<T, JsonObject>::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 <typename T>
@ -92,7 +104,7 @@ typename Internals::enable_if<Internals::is_same<T, JsonVariant>::value,
JsonVariant>::type
JsonVariant::to() {
if (!_data) return JsonVariant();
_data->setNull();
_data->type = Internals::JSON_NULL;
return *this;
}

View File

@ -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<char *>()) return DeserializationError::NotSupported;
JsonVariant value = object.set(keyData.asString());
JsonVariant value = object.set(key.as<char *>());
if (value.isInvalid()) return DeserializationError::NoMemory;
err = parse(value);

View File

@ -39,7 +39,7 @@ class StaticJsonDocument {
void clear() {
_memoryPool.clear();
_rootData.setNull();
_rootData.type = Internals::JSON_NULL;
}
size_t memoryUsage() const {

View File

@ -8,6 +8,7 @@ add_executable(JsonVariantTests
copy.cpp
is.cpp
isnull.cpp
misc.cpp
or.cpp
set_get.cpp
subscript.cpp

50
test/JsonVariant/misc.cpp Normal file
View File

@ -0,0 +1,50 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
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<JsonArray>();
arr.add(12);
arr.add(34);
JsonVariant v = arr;
REQUIRE(v.is<JsonArray>() == 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<JsonObject>();
obj["a"] = 12;
obj["b"] = 34;
JsonVariant v = obj;
REQUIRE(v.is<JsonObject>() == true);
REQUIRE(v.size() == 2);
REQUIRE(v["a"] == 12);
REQUIRE(v["b"] == 34);
}
}

View File

@ -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<JsonVariant>();
SECTION("std::string") {
v.set(std::string("hello"));
REQUIRE(v.isNull());
}
SECTION("Serialized<std::string>") {
v.set(serialized(std::string("hello")));
REQUIRE(v.isNull());
}
}