Changed the array subscript to automatically add missing elements

This commit is contained in:
Benoit Blanchon
2020-02-20 08:59:25 +01:00
parent 0001dabfd1
commit d8724e0a0b
24 changed files with 209 additions and 93 deletions

View File

@ -6,6 +6,7 @@ HEAD
* Added `DeserializationOption::Filter` (issue #959)
* Added example `JsonFilterExample.ino`
* Changed the array subscript operator to automatically add missing elements
* Fixed "deprecated-copy" warning on GCC 9 (fixes #1184)
v6.14.1 (2020-01-27)

View File

@ -33,12 +33,9 @@ void setup() {
"1000000,\"timezone\":0,\"sunrise\":1581492085,\"sunset\":1581527294}}");
// The filter: it contains "true" for each value we want to keep
const __FlashStringHelper* filter_json =
F("{\"list\":[{\"dt\":true,\"main\":{\"temp\":true}]}");
// Create the filter document
StaticJsonDocument<200> filter;
deserializeJson(filter, filter_json);
filter["list"][0]["dt"] = true;
filter["list"][0]["main"]["temp"] = true;
// Deserialize the document
StaticJsonDocument<400> doc;

View File

@ -9,7 +9,6 @@ using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("ElementProxy::set()") {
DynamicJsonDocument doc(4096);
doc.addElement();
ElementProxy<JsonDocument&> ep = doc[0];
SECTION("set(int)") {

View File

@ -0,0 +1,25 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("MemberProxy::operator[]") {
DynamicJsonDocument doc(4096);
ElementProxy<JsonDocument&> ep = doc[1];
SECTION("set member") {
ep["world"] = 42;
REQUIRE(doc.as<std::string>() == "[null,{\"world\":42}]");
}
SECTION("set element") {
ep[2] = 42;
REQUIRE(doc.as<std::string>() == "[null,[null,null,42]]");
}
}

File diff suppressed because one or more lines are too long

View File

@ -9,7 +9,20 @@
TEST_CASE("JsonArray::operator[]") {
DynamicJsonDocument doc(4096);
JsonArray array = doc.to<JsonArray>();
array.add(0);
SECTION("Pad with null") {
array[2] = 2;
array[5] = 5;
REQUIRE(array.size() == 6);
REQUIRE(array[0].isNull() == true);
REQUIRE(array[1].isNull() == true);
REQUIRE(array[2].isNull() == false);
REQUIRE(array[3].isNull() == true);
REQUIRE(array[4].isNull() == true);
REQUIRE(array[5].isNull() == false);
REQUIRE(array[2] == 2);
REQUIRE(array[5] == 5);
}
SECTION("int") {
array[0] = 123;

View File

@ -43,3 +43,11 @@ TEST_CASE("JsonDocument automatically promotes to object") {
REQUIRE(doc["one"]["two"]["three"] == 4);
}
TEST_CASE("JsonDocument automatically promotes to array") {
DynamicJsonDocument doc(4096);
doc[2] = 2;
REQUIRE(doc.as<std::string>() == "[null,null,2]");
}

View File

@ -43,10 +43,10 @@ TEST_CASE("JsonVariant::operator[]") {
SECTION("set value") {
array.add("hello");
var[0] = "world";
var[1] = "world";
REQUIRE(1 == var.size());
REQUIRE(std::string("world") == var[0]);
REQUIRE(var.size() == 2);
REQUIRE(std::string("world") == var[1]);
}
SECTION("set value in a nested object") {

View File

@ -11,9 +11,15 @@ TEST_CASE("MemberProxy::operator[]") {
DynamicJsonDocument doc(4096);
MemberProxy<JsonDocument&, const char*> mp = doc["hello"];
SECTION("set integer") {
SECTION("set member") {
mp["world"] = 42;
REQUIRE(doc.as<std::string>() == "{\"hello\":{\"world\":42}}");
}
SECTION("set element") {
mp[2] = 42;
REQUIRE(doc.as<std::string>() == "{\"hello\":[null,null,42]}");
}
}

View File

@ -9,7 +9,7 @@
namespace ARDUINOJSON_NAMESPACE {
inline VariantData *arrayAdd(CollectionData *arr, MemoryPool *pool) {
return arr ? arr->add(pool) : 0;
return arr ? arr->addElement(pool) : 0;
}
template <typename Visitor>

View File

@ -87,7 +87,7 @@ class ArrayConstRef : public ArrayRefBase<const CollectionData>,
}
FORCE_INLINE VariantConstRef getElement(size_t index) const {
return VariantConstRef(_data ? _data->get(index) : 0);
return VariantConstRef(_data ? _data->getElement(index) : 0);
}
};
@ -137,23 +137,28 @@ class ArrayRef : public ArrayRefBase<CollectionData>,
return arrayEquals(_data, rhs._data);
}
// Internal use
FORCE_INLINE VariantRef getOrAddElement(size_t index) const {
return VariantRef(_pool, _data ? _data->getOrAddElement(index, _pool) : 0);
}
// Gets the value at the specified index.
FORCE_INLINE VariantRef getElement(size_t index) const {
return VariantRef(_pool, _data ? _data->get(index) : 0);
return VariantRef(_pool, _data ? _data->getElement(index) : 0);
}
// Removes element at specified position.
FORCE_INLINE void remove(iterator it) const {
if (!_data)
return;
_data->remove(it.internal());
_data->removeSlot(it.internal());
}
// Removes element at specified index.
FORCE_INLINE void remove(size_t index) const {
if (!_data)
return;
_data->remove(index);
_data->removeElement(index);
}
private:

View File

@ -28,7 +28,7 @@ class ElementProxy : public VariantOperators<ElementProxy<TArray> >,
: _array(src._array), _index(src._index) {}
FORCE_INLINE this_type& operator=(const this_type& src) {
getUpstreamElement().set(src.as<VariantConstRef>());
getOrAddUpstreamElement().set(src.as<VariantConstRef>());
return *this;
}
@ -39,7 +39,7 @@ class ElementProxy : public VariantOperators<ElementProxy<TArray> >,
// std::string, String, ArrayRef, ObjectRef
template <typename T>
FORCE_INLINE this_type& operator=(const T& src) {
getUpstreamElement().set(src);
getOrAddUpstreamElement().set(src);
return *this;
}
//
@ -47,7 +47,7 @@ class ElementProxy : public VariantOperators<ElementProxy<TArray> >,
// TValue = char*, const char*, const __FlashStringHelper*
template <typename T>
FORCE_INLINE this_type& operator=(T* src) {
getUpstreamElement().set(src);
getOrAddUpstreamElement().set(src);
return *this;
}
@ -79,7 +79,7 @@ class ElementProxy : public VariantOperators<ElementProxy<TArray> >,
template <typename T>
FORCE_INLINE typename VariantTo<T>::type to() const {
return getUpstreamElement().template to<T>();
return getOrAddUpstreamElement().template to<T>();
}
// Replaces the value
@ -89,14 +89,14 @@ class ElementProxy : public VariantOperators<ElementProxy<TArray> >,
// std::string, String, ArrayRef, ObjectRef
template <typename TValue>
FORCE_INLINE bool set(const TValue& value) const {
return getUpstreamElement().set(value);
return getOrAddUpstreamElement().set(value);
}
//
// bool set(TValue)
// TValue = char*, const char*, const __FlashStringHelper*
template <typename TValue>
FORCE_INLINE bool set(TValue* value) const {
return getUpstreamElement().set(value);
return getOrAddUpstreamElement().set(value);
}
template <typename Visitor>
@ -120,20 +120,20 @@ class ElementProxy : public VariantOperators<ElementProxy<TArray> >,
template <typename TNestedKey>
VariantRef getOrAddMember(TNestedKey* key) const {
return getUpstreamElement().getOrAddMember(key);
return getOrAddUpstreamElement().getOrAddMember(key);
}
template <typename TNestedKey>
VariantRef getOrAddMember(const TNestedKey& key) const {
return getUpstreamElement().getOrAddMember(key);
return getOrAddUpstreamElement().getOrAddMember(key);
}
VariantRef addElement() const {
return getUpstreamElement().addElement();
return getOrAddUpstreamElement().addElement();
}
VariantRef getElement(size_t index) const {
return getUpstreamElement().getElement(index);
return getOrAddUpstreamElement().getElement(index);
}
FORCE_INLINE void remove(size_t index) const {
@ -160,6 +160,10 @@ class ElementProxy : public VariantOperators<ElementProxy<TArray> >,
return _array.getElement(_index);
}
FORCE_INLINE VariantRef getOrAddUpstreamElement() const {
return _array.getOrAddElement(_index);
}
TArray _array;
const size_t _index;
};

View File

@ -25,45 +25,56 @@ class CollectionData {
// - no destructor
// - no virtual
// - no inheritance
VariantSlot *addSlot(MemoryPool *);
VariantData *add(MemoryPool *pool);
// Array only
VariantData *addElement(MemoryPool *pool);
VariantData *getElement(size_t index) const;
VariantData *getOrAddElement(size_t index, MemoryPool *pool);
void removeElement(size_t index);
bool equalsArray(const CollectionData &other) const;
// Object only
template <typename TAdaptedString>
VariantData *add(TAdaptedString key, MemoryPool *pool);
VariantData *addMember(TAdaptedString key, MemoryPool *pool);
void clear();
template <typename TAdaptedString>
VariantData *getMember(TAdaptedString key) const;
template <typename TAdaptedString>
VariantData *getOrAddMember(TAdaptedString key, MemoryPool *pool);
template <typename TAdaptedString>
void removeMember(TAdaptedString key) {
removeSlot(getSlot(key));
}
template <typename TAdaptedString>
bool containsKey(const TAdaptedString &key) const;
bool copyFrom(const CollectionData &src, MemoryPool *pool);
bool equalsArray(const CollectionData &other) const;
bool equalsObject(const CollectionData &other) const;
VariantData *get(size_t index) const;
// Generic
template <typename TAdaptedString>
VariantData *get(TAdaptedString key) const;
void clear();
size_t memoryUsage() const;
size_t nesting() const;
size_t size() const;
VariantSlot *addSlot(MemoryPool *);
void removeSlot(VariantSlot *slot);
bool copyFrom(const CollectionData &src, MemoryPool *pool);
VariantSlot *head() const {
return _head;
}
void remove(size_t index);
template <typename TAdaptedString>
void remove(TAdaptedString key) {
remove(getSlot(key));
}
void remove(VariantSlot *slot);
size_t memoryUsage() const;
size_t nesting() const;
size_t size() const;
void movePointers(ptrdiff_t stringDistance, ptrdiff_t variantDistance);
private:

View File

@ -26,12 +26,13 @@ inline VariantSlot* CollectionData::addSlot(MemoryPool* pool) {
return slot;
}
inline VariantData* CollectionData::add(MemoryPool* pool) {
inline VariantData* CollectionData::addElement(MemoryPool* pool) {
return slotData(addSlot(pool));
}
template <typename TAdaptedString>
inline VariantData* CollectionData::add(TAdaptedString key, MemoryPool* pool) {
inline VariantData* CollectionData::addMember(TAdaptedString key,
MemoryPool* pool) {
VariantSlot* slot = addSlot(pool);
if (!slotSetKey(slot, key, pool))
return 0;
@ -55,11 +56,11 @@ inline bool CollectionData::copyFrom(const CollectionData& src,
VariantData* var;
if (s->key() != 0) {
if (s->ownsKey())
var = add(RamStringAdapter(s->key()), pool);
var = addMember(RamStringAdapter(s->key()), pool);
else
var = add(ConstRamStringAdapter(s->key()), pool);
var = addMember(ConstRamStringAdapter(s->key()), pool);
} else {
var = add(pool);
var = addElement(pool);
}
if (!var)
return false;
@ -73,7 +74,7 @@ inline bool CollectionData::equalsObject(const CollectionData& other) const {
size_t count = 0;
for (VariantSlot* slot = _head; slot; slot = slot->next()) {
VariantData* v1 = slot->data();
VariantData* v2 = other.get(adaptString(slot->key()));
VariantData* v2 = other.getMember(adaptString(slot->key()));
if (!variantEquals(v1, v2))
return false;
count++;
@ -123,17 +124,40 @@ inline VariantSlot* CollectionData::getPreviousSlot(VariantSlot* target) const {
}
template <typename TAdaptedString>
inline VariantData* CollectionData::get(TAdaptedString key) const {
inline VariantData* CollectionData::getMember(TAdaptedString key) const {
VariantSlot* slot = getSlot(key);
return slot ? slot->data() : 0;
}
inline VariantData* CollectionData::get(size_t index) const {
template <typename TAdaptedString>
inline VariantData* CollectionData::getOrAddMember(TAdaptedString key,
MemoryPool* pool) {
VariantSlot* slot = getSlot(key);
return slot ? slot->data() : addMember(key, pool);
}
inline VariantData* CollectionData::getElement(size_t index) const {
VariantSlot* slot = getSlot(index);
return slot ? slot->data() : 0;
}
inline void CollectionData::remove(VariantSlot* slot) {
inline VariantData* CollectionData::getOrAddElement(size_t index,
MemoryPool* pool) {
VariantSlot* slot = _head;
while (slot && index > 0) {
slot = slot->next();
index--;
}
if (!slot)
index++;
while (index > 0) {
slot = addSlot(pool);
index--;
}
return slotData(slot);
}
inline void CollectionData::removeSlot(VariantSlot* slot) {
if (!slot)
return;
VariantSlot* prev = getPreviousSlot(slot);
@ -146,8 +170,8 @@ inline void CollectionData::remove(VariantSlot* slot) {
_tail = prev;
}
inline void CollectionData::remove(size_t index) {
remove(getSlot(index));
inline void CollectionData::removeElement(size_t index) {
removeSlot(getSlot(index));
}
inline size_t CollectionData::memoryUsage() const {

View File

@ -192,6 +192,10 @@ class JsonDocument : public Visitable {
return VariantConstRef(_data.getElement(index));
}
FORCE_INLINE VariantRef getOrAddElement(size_t index) {
return VariantRef(&_pool, _data.getOrAddElement(index, &_pool));
}
// JsonVariantConst getMember(char*) const
// JsonVariantConst getMember(const char*) const
// JsonVariantConst getMember(const __FlashStringHelper*) const

View File

@ -139,7 +139,7 @@ class JsonDeserializer {
for (;;) {
if (memberFilter.allow()) {
// Allocate slot in array
VariantData *value = array.add(_pool);
VariantData *value = array.addElement(_pool);
if (!value)
return DeserializationError::NoMemory;
@ -231,7 +231,7 @@ class JsonDeserializer {
TFilter memberFilter = filter[key];
if (memberFilter.allow()) {
VariantData *variant = object.get(adaptString(key));
VariantData *variant = object.getMember(adaptString(key));
if (!variant) {
// Allocate slot in object
VariantSlot *slot = object.addSlot(_pool);

View File

@ -269,7 +269,7 @@ class MsgPackDeserializer {
return DeserializationError::TooDeep;
for (; n; --n) {
VariantData *value = array.add(_pool);
VariantData *value = array.addElement(_pool);
if (!value)
return DeserializationError::NoMemory;

View File

@ -129,11 +129,14 @@ class MemberProxy : public VariantOperators<MemberProxy<TObject, TStringRef> >,
return getOrAddUpstreamMember().addElement();
}
// getElement(size_t) const
FORCE_INLINE VariantRef getElement(size_t index) const {
return getUpstreamMember().getElement(index);
}
FORCE_INLINE VariantRef getOrAddElement(size_t index) const {
return getOrAddUpstreamMember().getOrAddElement(index);
}
// getMember(char*) const
// getMember(const char*) const
// getMember(const __FlashStringHelper*) const

View File

@ -28,14 +28,14 @@ template <typename TAdaptedString>
inline VariantData *objectGet(const CollectionData *obj, TAdaptedString key) {
if (!obj)
return 0;
return obj->get(key);
return obj->getMember(key);
}
template <typename TAdaptedString>
void objectRemove(CollectionData *obj, TAdaptedString key) {
if (!obj)
return;
obj->remove(key);
obj->removeMember(key);
}
template <typename TAdaptedString>
@ -49,10 +49,10 @@ inline VariantData *objectGetOrCreate(CollectionData *obj, TAdaptedString key,
return 0;
// search a matching key
VariantData *var = obj->get(key);
VariantData *var = obj->getMember(key);
if (var)
return var;
return obj->add(key, pool);
return obj->addMember(key, pool);
}
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -213,7 +213,7 @@ class ObjectRef : public ObjectRefBase<CollectionData>,
FORCE_INLINE void remove(iterator it) const {
if (!_data)
return;
_data->remove(it.internal());
_data->removeSlot(it.internal());
}
// remove(const std::string&) const

View File

@ -186,13 +186,13 @@ class VariantData {
void remove(size_t index) {
if (isArray())
_content.asCollection.remove(index);
_content.asCollection.removeElement(index);
}
template <typename TAdaptedString>
void remove(TAdaptedString key) {
if (isObject())
_content.asCollection.remove(key);
_content.asCollection.removeMember(key);
}
void setBoolean(bool value) {
@ -335,16 +335,24 @@ class VariantData {
toArray();
if (!isArray())
return 0;
return _content.asCollection.add(pool);
return _content.asCollection.addElement(pool);
}
VariantData *getElement(size_t index) const {
return isArray() ? _content.asCollection.get(index) : 0;
return isArray() ? _content.asCollection.getElement(index) : 0;
}
VariantData *getOrAddElement(size_t index, MemoryPool *pool) {
if (isNull())
toArray();
if (!isArray())
return 0;
return _content.asCollection.getOrAddElement(index, pool);
}
template <typename TAdaptedString>
VariantData *getMember(TAdaptedString key) const {
return isObject() ? _content.asCollection.get(key) : 0;
return isObject() ? _content.asCollection.getMember(key) : 0;
}
template <typename TAdaptedString>
@ -353,10 +361,7 @@ class VariantData {
toObject();
if (!isObject())
return 0;
VariantData *var = _content.asCollection.get(key);
if (var)
return var;
return _content.asCollection.add(key, pool);
return _content.asCollection.getOrAddMember(key, pool);
}
void movePointers(ptrdiff_t stringDistance, ptrdiff_t variantDistance) {

View File

@ -160,18 +160,26 @@ inline CollectionData *variantToObject(VariantData *var) {
return &var->toObject();
}
inline NO_INLINE VariantData *variantAdd(VariantData *var, MemoryPool *pool) {
inline NO_INLINE VariantData *variantAddElement(VariantData *var,
MemoryPool *pool) {
return var != 0 ? var->addElement(pool) : 0;
}
inline NO_INLINE VariantData *variantGetOrAddElement(VariantData *var,
size_t index,
MemoryPool *pool) {
return var != 0 ? var->getOrAddElement(index, pool) : 0;
}
template <typename TChar>
NO_INLINE VariantData *variantGetOrCreate(VariantData *var, TChar *key,
NO_INLINE VariantData *variantGetOrAddMember(VariantData *var, TChar *key,
MemoryPool *pool) {
return var != 0 ? var->getOrAddMember(adaptString(key), pool) : 0;
}
template <typename TString>
NO_INLINE VariantData *variantGetOrCreate(VariantData *var, const TString &key,
NO_INLINE VariantData *variantGetOrAddMember(VariantData *var,
const TString &key,
MemoryPool *pool) {
return var != 0 ? var->getOrAddMember(adaptString(key), pool) : 0;
}

View File

@ -119,13 +119,17 @@ inline VariantConstRef VariantConstRef::getElement(size_t index) const {
}
inline VariantRef VariantRef::addElement() const {
return VariantRef(_pool, variantAdd(_data, _pool));
return VariantRef(_pool, variantAddElement(_data, _pool));
}
inline VariantRef VariantRef::getElement(size_t index) const {
return VariantRef(_pool, _data != 0 ? _data->getElement(index) : 0);
}
inline VariantRef VariantRef::getOrAddElement(size_t index) const {
return VariantRef(_pool, variantGetOrAddElement(_data, index, _pool));
}
template <typename TChar>
inline VariantRef VariantRef::getMember(TChar *key) const {
return VariantRef(_pool, _data != 0 ? _data->getMember(adaptString(key)) : 0);
@ -139,11 +143,11 @@ VariantRef::getMember(const TString &key) const {
template <typename TChar>
inline VariantRef VariantRef::getOrAddMember(TChar *key) const {
return VariantRef(_pool, variantGetOrCreate(_data, key, _pool));
return VariantRef(_pool, variantGetOrAddMember(_data, key, _pool));
}
template <typename TString>
inline VariantRef VariantRef::getOrAddMember(const TString &key) const {
return VariantRef(_pool, variantGetOrCreate(_data, key, _pool));
return VariantRef(_pool, variantGetOrAddMember(_data, key, _pool));
}
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -301,6 +301,8 @@ class VariantRef : public VariantRefBase<VariantData>,
FORCE_INLINE VariantRef getElement(size_t) const;
FORCE_INLINE VariantRef getOrAddElement(size_t) const;
// getMember(const char*) const
// getMember(const __FlashStringHelper*) const
template <typename TChar>
@ -391,7 +393,7 @@ class VariantConstRef : public VariantRefBase<const VariantData>,
template <typename TChar>
FORCE_INLINE VariantConstRef getMember(TChar *key) const {
const CollectionData *obj = variantAsObject(_data);
return VariantConstRef(obj ? obj->get(adaptString(key)) : 0);
return VariantConstRef(obj ? obj->getMember(adaptString(key)) : 0);
}
// operator[](const std::string&) const