Create or assign a JsonDocument from a JsonArray/JsonObject/JsonVariant

This commit is contained in:
Benoit Blanchon
2019-01-23 10:47:20 +01:00
parent 2a3b51ac3a
commit 4167b11434
10 changed files with 334 additions and 98 deletions

View File

@ -15,6 +15,9 @@ HEAD
* Replaced `JsonDocument::nestingLimit` with an additional parameter * Replaced `JsonDocument::nestingLimit` with an additional parameter
to `deserializeJson()` and `deserializeMsgPack()` to `deserializeJson()` and `deserializeMsgPack()`
* Fixed uninitialized variant in `JsonDocument` * Fixed uninitialized variant in `JsonDocument`
* Fixed `StaticJsonDocument` copy constructor and copy assignment
* The copy constructor of `DynamicJsonDocument` chooses the capacity according to the memory usage of the source, not from the capacity of the source.
* Added the ability to create/assign a `StaticJsonDocument`/`DynamicJsonDocument` from a `JsonArray`/`JsonObject`/`JsonVariant`
* Added `JsonDocument::isNull()` * Added `JsonDocument::isNull()`
> ### BREAKING CHANGES > ### BREAKING CHANGES

View File

@ -13,16 +13,24 @@ namespace ARDUINOJSON_NAMESPACE {
class DynamicJsonDocument : public JsonDocument { class DynamicJsonDocument : public JsonDocument {
public: public:
explicit DynamicJsonDocument(size_t capa) explicit DynamicJsonDocument(size_t capa)
: JsonDocument(allocPool(addPadding(capa))) {} : JsonDocument(allocPool(capa)) {}
DynamicJsonDocument(const DynamicJsonDocument& src) DynamicJsonDocument(const DynamicJsonDocument& src)
: JsonDocument(allocPool(src.capacity())) { : JsonDocument(allocPool(src.memoryUsage())) {
copy(src); set(src);
} }
DynamicJsonDocument(const JsonDocument& src) template <typename T>
: JsonDocument(allocPool(src.capacity())) { DynamicJsonDocument(const T& src,
copy(src); typename enable_if<IsVisitable<T>::value>::type* = 0)
: JsonDocument(allocPool(src.memoryUsage())) {
set(src);
}
// disambiguate
DynamicJsonDocument(VariantRef src)
: JsonDocument(allocPool(src.memoryUsage())) {
set(src);
} }
~DynamicJsonDocument() { ~DynamicJsonDocument() {
@ -31,19 +39,20 @@ class DynamicJsonDocument : public JsonDocument {
DynamicJsonDocument& operator=(const DynamicJsonDocument& src) { DynamicJsonDocument& operator=(const DynamicJsonDocument& src) {
reallocPoolIfTooSmall(src.memoryUsage()); reallocPoolIfTooSmall(src.memoryUsage());
copy(src); set(src);
return *this; return *this;
} }
template <typename T> template <typename T>
DynamicJsonDocument& operator=(const JsonDocument& src) { DynamicJsonDocument& operator=(const T& src) {
reallocPoolIfTooSmall(src.memoryUsage()); reallocPoolIfTooSmall(src.memoryUsage());
copy(src); set(src);
return *this; return *this;
} }
private: private:
MemoryPool allocPool(size_t capa) { MemoryPool allocPool(size_t requiredSize) {
size_t capa = addPadding(requiredSize);
return MemoryPool(reinterpret_cast<char*>(malloc(capa)), capa); return MemoryPool(reinterpret_cast<char*>(malloc(capa)), capa);
} }

View File

@ -5,6 +5,7 @@
#pragma once #pragma once
#include "../Memory/MemoryPool.hpp" #include "../Memory/MemoryPool.hpp"
#include "../Object/ObjectRef.hpp"
#include "../Variant/VariantRef.hpp" #include "../Variant/VariantRef.hpp"
#include "../Variant/VariantTo.hpp" #include "../Variant/VariantTo.hpp"
@ -53,6 +54,16 @@ class JsonDocument : public Visitable {
return _pool.capacity(); return _pool.capacity();
} }
bool set(const JsonDocument& src) {
return to<VariantRef>().set(src.as<VariantRef>());
}
template <typename T>
typename enable_if<!is_base_of<JsonDocument, T>::value, bool>::type set(
const T& src) {
return to<VariantRef>().set(src);
}
template <typename T> template <typename T>
typename VariantTo<T>::type to() { typename VariantTo<T>::type to() {
clear(); clear();

View File

@ -16,13 +16,31 @@ class StaticJsonDocument : public JsonDocument {
public: public:
StaticJsonDocument() : JsonDocument(_buffer, ACTUAL_CAPACITY) {} StaticJsonDocument() : JsonDocument(_buffer, ACTUAL_CAPACITY) {}
StaticJsonDocument(const JsonDocument& src) StaticJsonDocument(const StaticJsonDocument& src)
: JsonDocument(_buffer, ACTUAL_CAPACITY) { : JsonDocument(_buffer, ACTUAL_CAPACITY) {
copy(src); set(src);
} }
StaticJsonDocument operator=(const JsonDocument& src) { template <typename T>
copy(src); StaticJsonDocument(const T& src,
typename enable_if<IsVisitable<T>::value>::type* = 0)
: JsonDocument(_buffer, ACTUAL_CAPACITY) {
set(src);
}
// disambiguate
StaticJsonDocument(VariantRef src) : JsonDocument(_buffer, ACTUAL_CAPACITY) {
set(src);
}
StaticJsonDocument operator=(const StaticJsonDocument& src) {
set(src);
return *this;
}
template <typename T>
StaticJsonDocument operator=(const T& src) {
set(src);
return *this; return *this;
} }

View File

@ -77,6 +77,14 @@ variantAs(const VariantData* _data) {
return _data != 0 ? _data->asString() : 0; return _data != 0 ? _data->asString() : 0;
} }
template <typename T>
inline typename enable_if<is_same<ArrayConstRef, T>::value, T>::type variantAs(
const VariantData* _data);
template <typename T>
inline typename enable_if<is_same<ObjectConstRef, T>::value, T>::type variantAs(
const VariantData* _data);
template <typename T> template <typename T>
inline typename enable_if<is_same<VariantConstRef, T>::value, T>::type inline typename enable_if<is_same<VariantConstRef, T>::value, T>::type
variantAs(const VariantData* _data); variantAs(const VariantData* _data);

View File

@ -5,10 +5,23 @@
#pragma once #pragma once
#include "../Serialization/DynamicStringWriter.hpp" #include "../Serialization/DynamicStringWriter.hpp"
#include "VariantFunctions.hpp"
#include "VariantRef.hpp" #include "VariantRef.hpp"
namespace ARDUINOJSON_NAMESPACE { namespace ARDUINOJSON_NAMESPACE {
template <typename T>
inline typename enable_if<is_same<ArrayConstRef, T>::value, T>::type variantAs(
const VariantData* _data) {
return ArrayConstRef(variantAsArray(_data));
}
template <typename T>
inline typename enable_if<is_same<ObjectConstRef, T>::value, T>::type variantAs(
const VariantData* _data) {
return ObjectConstRef(variantAsObject(_data));
}
template <typename T> template <typename T>
inline typename enable_if<is_same<VariantConstRef, T>::value, T>::type inline typename enable_if<is_same<VariantConstRef, T>::value, T>::type
variantAs(const VariantData* _data) { variantAs(const VariantData* _data) {

View File

@ -67,10 +67,7 @@ class VariantData {
} }
CollectionData *asArray() { CollectionData *asArray() {
if (type() == VALUE_IS_ARRAY) return type() == VALUE_IS_ARRAY ? &_content.asCollection : 0;
return &_content.asCollection;
else
return 0;
} }
const CollectionData *asArray() const { const CollectionData *asArray() const {
@ -78,10 +75,7 @@ class VariantData {
} }
CollectionData *asObject() { CollectionData *asObject() {
if (type() == VALUE_IS_OBJECT) return type() == VALUE_IS_OBJECT ? &_content.asCollection : 0;
return &_content.asCollection;
else
return 0;
} }
const CollectionData *asObject() const { const CollectionData *asObject() const {

View File

@ -16,6 +16,10 @@ inline void variantAccept(const VariantData *var, Visitor &visitor) {
visitor.visitNull(); visitor.visitNull();
} }
inline const CollectionData *variantAsArray(const VariantData *var) {
return var != 0 ? var->asArray() : 0;
}
inline const CollectionData *variantAsObject(const VariantData *var) { inline const CollectionData *variantAsObject(const VariantData *var) {
return var != 0 ? var->asObject() : 0; return var != 0 ? var->asObject() : 0;
} }

View File

@ -5,6 +5,14 @@
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <catch.hpp> #include <catch.hpp>
using ARDUINOJSON_NAMESPACE::addPadding;
static void REQUIRE_JSON(JsonDocument& doc, const std::string& expected) {
std::string json;
serializeJson(doc, json);
REQUIRE(json == expected);
}
TEST_CASE("DynamicJsonDocument") { TEST_CASE("DynamicJsonDocument") {
DynamicJsonDocument doc(4096); DynamicJsonDocument doc(4096);
@ -74,20 +82,62 @@ TEST_CASE("DynamicJsonDocument") {
} }
} }
TEST_CASE("DynamicJsonDocument copies") { TEST_CASE("DynamicJsonDocument constructor") {
SECTION("Copy constructor") { SECTION("Copy constructor") {
DynamicJsonDocument doc1(1234); DynamicJsonDocument doc1(1234);
deserializeJson(doc1, "{\"hello\":\"world\"}"); deserializeJson(doc1, "{\"hello\":\"world\"}");
DynamicJsonDocument doc2 = doc1; DynamicJsonDocument doc2 = doc1;
std::string json; REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
serializeJson(doc2, json);
REQUIRE(json == "{\"hello\":\"world\"}");
REQUIRE(doc2.capacity() == doc1.capacity()); REQUIRE(doc2.capacity() == addPadding(doc1.memoryUsage()));
} }
SECTION("Construct from StaticJsonDocument") {
StaticJsonDocument<200> doc1;
deserializeJson(doc1, "{\"hello\":\"world\"}");
DynamicJsonDocument doc2 = doc1;
REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
REQUIRE(doc2.capacity() == addPadding(doc1.memoryUsage()));
}
SECTION("Construct from JsonObject") {
StaticJsonDocument<200> doc1;
JsonObject obj = doc1.to<JsonObject>();
obj["hello"] = "world";
DynamicJsonDocument doc2 = obj;
REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
REQUIRE(doc2.capacity() == addPadding(doc1.memoryUsage()));
}
SECTION("Construct from JsonArray") {
StaticJsonDocument<200> doc1;
JsonArray arr = doc1.to<JsonArray>();
arr.add("hello");
DynamicJsonDocument doc2 = arr;
REQUIRE_JSON(doc2, "[\"hello\"]");
REQUIRE(doc2.capacity() == addPadding(doc1.memoryUsage()));
}
SECTION("Construct from JsonVariant") {
StaticJsonDocument<200> doc1;
deserializeJson(doc1, "42");
DynamicJsonDocument doc2 = doc1.as<JsonVariant>();
REQUIRE_JSON(doc2, "42");
REQUIRE(doc2.capacity() == addPadding(doc1.memoryUsage()));
}
}
TEST_CASE("DynamicJsonDocument assignment") {
SECTION("Copy assignment preserves the buffer when capacity is sufficient") { SECTION("Copy assignment preserves the buffer when capacity is sufficient") {
DynamicJsonDocument doc1(1234); DynamicJsonDocument doc1(1234);
deserializeJson(doc1, "{\"hello\":\"world\"}"); deserializeJson(doc1, "{\"hello\":\"world\"}");
@ -95,9 +145,7 @@ TEST_CASE("DynamicJsonDocument copies") {
DynamicJsonDocument doc2(doc1.capacity()); DynamicJsonDocument doc2(doc1.capacity());
doc2 = doc1; doc2 = doc1;
std::string json; REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
serializeJson(doc2, json);
REQUIRE(json == "{\"hello\":\"world\"}");
REQUIRE(doc2.capacity() == doc1.capacity()); REQUIRE(doc2.capacity() == doc1.capacity());
} }
@ -110,34 +158,52 @@ TEST_CASE("DynamicJsonDocument copies") {
doc2 = doc1; doc2 = doc1;
REQUIRE(doc2.capacity() >= doc1.memoryUsage()); REQUIRE(doc2.capacity() >= doc1.memoryUsage());
std::string json; REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
serializeJson(doc2, json);
REQUIRE(json == "{\"hello\":\"world\"}");
}
SECTION("Construct from StaticJsonDocument") {
StaticJsonDocument<200> sdoc;
deserializeJson(sdoc, "{\"hello\":\"world\"}");
DynamicJsonDocument ddoc = sdoc;
std::string json;
serializeJson(ddoc, json);
REQUIRE(json == "{\"hello\":\"world\"}");
REQUIRE(ddoc.capacity() == sdoc.capacity());
} }
SECTION("Assign from StaticJsonDocument") { SECTION("Assign from StaticJsonDocument") {
DynamicJsonDocument ddoc(4096); StaticJsonDocument<200> doc1;
ddoc.to<JsonVariant>().set(666); deserializeJson(doc1, "{\"hello\":\"world\"}");
DynamicJsonDocument doc2(4096);
doc2.to<JsonVariant>().set(666);
StaticJsonDocument<200> sdoc; doc2 = doc1;
deserializeJson(sdoc, "{\"hello\":\"world\"}");
ddoc = sdoc; REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
}
std::string json; SECTION("Assign from JsonObject") {
serializeJson(ddoc, json); StaticJsonDocument<200> doc1;
REQUIRE(json == "{\"hello\":\"world\"}"); JsonObject obj = doc1.to<JsonObject>();
obj["hello"] = "world";
DynamicJsonDocument doc2(4096);
doc2 = obj;
REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
REQUIRE(doc2.capacity() == 4096);
}
SECTION("Assign from JsonArray") {
StaticJsonDocument<200> doc1;
JsonArray arr = doc1.to<JsonArray>();
arr.add("hello");
DynamicJsonDocument doc2(4096);
doc2 = arr;
REQUIRE_JSON(doc2, "[\"hello\"]");
REQUIRE(doc2.capacity() == 4096);
}
SECTION("Assign from JsonVariant") {
StaticJsonDocument<200> doc1;
deserializeJson(doc1, "42");
DynamicJsonDocument doc2(4096);
doc2 = doc1.as<JsonVariant>();
REQUIRE_JSON(doc2, "42");
REQUIRE(doc2.capacity() == 4096);
} }
} }

View File

@ -5,6 +5,12 @@
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <catch.hpp> #include <catch.hpp>
static void REQUIRE_JSON(JsonDocument& doc, const std::string& expected) {
std::string json;
serializeJson(doc, json);
REQUIRE(json == expected);
}
TEST_CASE("StaticJsonDocument") { TEST_CASE("StaticJsonDocument") {
SECTION("capacity()") { SECTION("capacity()") {
SECTION("matches template argument") { SECTION("matches template argument") {
@ -36,67 +42,171 @@ TEST_CASE("StaticJsonDocument") {
doc1 = doc2; doc1 = doc2;
std::string json; REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
serializeJson(doc1, json);
REQUIRE(json == "{\"hello\":\"world\"}");
} }
SECTION("Contructor") {
SECTION("Copy constructor") { SECTION("Copy constructor") {
StaticJsonDocument<200> doc1; StaticJsonDocument<200> doc1;
deserializeJson(doc1, "{\"hello\":\"world\"}"); deserializeJson(doc1, "{\"hello\":\"world\"}");
StaticJsonDocument<200> doc2 = doc1; StaticJsonDocument<200> doc2 = doc1;
std::string json; deserializeJson(doc1, "{\"HELLO\":\"WORLD\"}");
serializeJson(doc2, json); REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
REQUIRE(json == "{\"hello\":\"world\"}"); }
SECTION("Construct from StaticJsonDocument of different size") {
StaticJsonDocument<300> doc1;
deserializeJson(doc1, "{\"hello\":\"world\"}");
StaticJsonDocument<200> doc2 = doc1;
REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
}
SECTION("Construct from DynamicJsonDocument") {
DynamicJsonDocument doc1(4096);
deserializeJson(doc1, "{\"hello\":\"world\"}");
StaticJsonDocument<200> doc2 = doc1;
REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
}
SECTION("Construct from JsonObject") {
DynamicJsonDocument doc1(4096);
deserializeJson(doc1, "{\"hello\":\"world\"}");
StaticJsonDocument<200> doc2 = doc1.as<JsonObject>();
deserializeJson(doc1, "{\"HELLO\":\"WORLD\"}");
REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
}
SECTION("Construct from JsonArray") {
DynamicJsonDocument doc1(4096);
deserializeJson(doc1, "[\"hello\",\"world\"]");
StaticJsonDocument<200> doc2 = doc1.as<JsonArray>();
deserializeJson(doc1, "[\"HELLO\",\"WORLD\"]");
REQUIRE_JSON(doc2, "[\"hello\",\"world\"]");
}
SECTION("Construct from JsonVariant") {
DynamicJsonDocument doc1(4096);
deserializeJson(doc1, "42");
StaticJsonDocument<200> doc2 = doc1.as<JsonVariant>();
REQUIRE_JSON(doc2, "42");
}
}
SECTION("Assignment") {
SECTION("Copy assignment") {
StaticJsonDocument<200> doc1, doc2;
doc1.to<JsonVariant>().set(666);
deserializeJson(doc1, "{\"hello\":\"world\"}");
doc2 = doc1;
deserializeJson(doc1, "{\"HELLO\":\"WORLD\"}");
REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
} }
SECTION("Assign from StaticJsonDocument of different capacity") { SECTION("Assign from StaticJsonDocument of different capacity") {
StaticJsonDocument<200> doc1; StaticJsonDocument<200> doc1;
StaticJsonDocument<300> doc2; StaticJsonDocument<300> doc2;
doc1.to<JsonVariant>().set(666); doc1.to<JsonVariant>().set(666);
deserializeJson(doc2, "{\"hello\":\"world\"}"); deserializeJson(doc1, "{\"hello\":\"world\"}");
doc1 = doc2; doc2 = doc1;
std::string json; REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
serializeJson(doc1, json);
REQUIRE(json == "{\"hello\":\"world\"}");
} }
SECTION("Assign from DynamicJsonDocument") { SECTION("Assign from DynamicJsonDocument") {
StaticJsonDocument<200> doc1; StaticJsonDocument<200> doc1;
DynamicJsonDocument doc2(4096); DynamicJsonDocument doc2(4096);
doc1.to<JsonVariant>().set(666); doc1.to<JsonVariant>().set(666);
deserializeJson(doc2, "{\"hello\":\"world\"}"); deserializeJson(doc1, "{\"hello\":\"world\"}");
doc1 = doc2; doc2 = doc1;
std::string json; deserializeJson(doc1, "{\"HELLO\":\"WORLD\"}");
serializeJson(doc1, json); REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
REQUIRE(json == "{\"hello\":\"world\"}");
} }
SECTION("Construct from StaticJsonDocument of different size") { SECTION("Assign from JsonArray") {
StaticJsonDocument<300> doc2; StaticJsonDocument<200> doc1;
deserializeJson(doc2, "{\"hello\":\"world\"}");
StaticJsonDocument<200> doc1 = doc2;
std::string json;
serializeJson(doc1, json);
REQUIRE(json == "{\"hello\":\"world\"}");
}
SECTION("Construct from DynamicJsonDocument") {
DynamicJsonDocument doc2(4096); DynamicJsonDocument doc2(4096);
deserializeJson(doc2, "{\"hello\":\"world\"}"); doc1.to<JsonVariant>().set(666);
deserializeJson(doc1, "[\"hello\",\"world\"]");
StaticJsonDocument<200> doc1 = doc2; doc2 = doc1.as<JsonArray>();
std::string json; deserializeJson(doc1, "[\"HELLO\",\"WORLD\"]");
serializeJson(doc1, json); REQUIRE_JSON(doc2, "[\"hello\",\"world\"]");
REQUIRE(json == "{\"hello\":\"world\"}"); }
SECTION("Assign from JsonArrayConst") {
StaticJsonDocument<200> doc1;
DynamicJsonDocument doc2(4096);
doc1.to<JsonVariant>().set(666);
deserializeJson(doc1, "[\"hello\",\"world\"]");
doc2 = doc1.as<JsonArrayConst>();
deserializeJson(doc1, "[\"HELLO\",\"WORLD\"]");
REQUIRE_JSON(doc2, "[\"hello\",\"world\"]");
}
SECTION("Assign from JsonObject") {
StaticJsonDocument<200> doc1;
DynamicJsonDocument doc2(4096);
doc1.to<JsonVariant>().set(666);
deserializeJson(doc1, "{\"hello\":\"world\"}");
doc2 = doc1.as<JsonObject>();
deserializeJson(doc1, "{\"HELLO\":\"WORLD\"}");
REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
}
SECTION("Assign from JsonObjectConst") {
StaticJsonDocument<200> doc1;
DynamicJsonDocument doc2(4096);
doc1.to<JsonVariant>().set(666);
deserializeJson(doc1, "{\"hello\":\"world\"}");
doc2 = doc1.as<JsonObjectConst>();
deserializeJson(doc1, "{\"HELLO\":\"WORLD\"}");
REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
}
SECTION("Assign from JsonVariant") {
DynamicJsonDocument doc1(4096);
doc1.to<JsonVariant>().set(666);
deserializeJson(doc1, "42");
StaticJsonDocument<200> doc2;
doc2 = doc1.as<JsonVariant>();
REQUIRE_JSON(doc2, "42");
}
SECTION("Assign from JsonVariantConst") {
DynamicJsonDocument doc1(4096);
doc1.to<JsonVariant>().set(666);
deserializeJson(doc1, "42");
StaticJsonDocument<200> doc2;
doc2 = doc1.as<JsonVariantConst>();
REQUIRE_JSON(doc2, "42");
}
} }
} }