Implemented reference semantics for JsonVariant

This commit is contained in:
Benoit Blanchon
2018-08-21 17:56:16 +02:00
parent 0454bd1ef6
commit 9cbc891816
53 changed files with 1196 additions and 839 deletions

View File

@ -1,6 +1,56 @@
ArduinoJson: change log ArduinoJson: change log
======================= =======================
HEAD
----
* Implemented reference semantics for `JsonVariant`
* Replace `JsonPair`'s `key` and `value` with `key()` and `value()`
> ### BREAKING CHANGES
>
> #### JsonVariant
>
> `JsonVariant` now has a semantic similar to `JsonObject` and `JsonArray`.
> It's a reference to a value stored in the `JsonDocument`.
> As a consequence, a `JsonVariant` cannot be used as a standalone variable anymore.
>
> Old code:
>
> ```c++
> JsonVariant myValue = 42;
> ```
>
> New code:
>
> ```c++
> DynamicJsonDocument doc;
> JsonVariant myValue = doc.to<JsonVariant>();
> myValue.set(42);
> ```
>
> #### JsonPair
>
> Old code:
>
> ```c++
> for(JsonPair p : myObject) {
> Serial.println(p.key);
> Serial.println(p.value.as<int>());
> }
> ```
>
> New code:
>
> ```c++
> for(JsonPair p : myObject) {
> Serial.println(p.key());
> Serial.println(p.value().as<int>());
> }
> ```
>
> CAUTION: the key is now read only!
v6.2.3-beta v6.2.3-beta
----------- -----------

View File

@ -4,6 +4,8 @@
#pragma once #pragma once
#include <stdlib.h> // size_t
#include "JsonFloat.hpp" #include "JsonFloat.hpp"
#include "JsonInteger.hpp" #include "JsonInteger.hpp"

View File

@ -0,0 +1,181 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#pragma once
#include "../Numbers/parseFloat.hpp"
#include "../Numbers/parseInteger.hpp"
#include "JsonVariantContent.hpp"
#include "JsonVariantType.hpp"
namespace ArduinoJson {
namespace Internals {
struct JsonVariantData {
JsonVariantType type;
JsonVariantContent content;
void setBoolean(bool value) {
type = JSON_BOOLEAN;
content.asInteger = static_cast<JsonUInt>(value);
}
void setFloat(JsonFloat value) {
type = JSON_FLOAT;
content.asFloat = value;
}
void setInteger(JsonInteger value) {
if (value > 0)
setPostiveInteger(static_cast<JsonUInt>(value));
else
setNegativeInteger(static_cast<JsonUInt>(-value));
}
void setNegativeInteger(JsonUInt value) {
type = JSON_NEGATIVE_INTEGER;
content.asInteger = value;
}
void setPostiveInteger(JsonUInt value) {
type = JSON_POSITIVE_INTEGER;
content.asInteger = value;
}
void setString(const char *value) {
type = JSON_STRING;
content.asString = value;
}
void setRaw(const char *data, size_t size) {
type = JSON_RAW;
content.asRaw.data = data;
content.asRaw.size = size;
}
void setNull() {
type = JSON_NULL;
}
void setArray(JsonArrayData &array) {
type = JSON_ARRAY;
content.asArray = &array;
}
void setObject(JsonObjectData &object) {
type = JSON_OBJECT;
content.asObject = &object;
}
JsonArrayData *asArray() const {
return type == JSON_ARRAY ? content.asArray : 0;
}
JsonObjectData *asObject() const {
return type == JSON_OBJECT ? content.asObject : 0;
}
template <typename T>
T asInteger() const {
switch (type) {
case JSON_NULL:
case JSON_RAW:
return 0;
case JSON_POSITIVE_INTEGER:
case JSON_BOOLEAN:
return T(content.asInteger);
case JSON_NEGATIVE_INTEGER:
return T(~content.asInteger + 1);
case JSON_STRING:
return parseInteger<T>(content.asString);
default:
return T(content.asFloat);
}
}
template <typename T>
T asFloat() const {
switch (type) {
case JSON_NULL:
case JSON_RAW:
return 0;
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_STRING:
return parseFloat<T>(content.asString);
default:
return static_cast<T>(content.asFloat);
}
}
const char *asString() const {
return type == JSON_STRING ? content.asString : NULL;
}
bool isArray() const {
return type == Internals::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 == Internals::JSON_OBJECT;
}
bool isString() const {
return type == Internals::JSON_STRING;
}
template <typename Visitor>
void visit(Visitor &visitor) const {
switch (type) {
case JSON_FLOAT:
return visitor.acceptFloat(content.asFloat);
case JSON_ARRAY:
return visitor.acceptArray(*content.asArray);
case JSON_OBJECT:
return visitor.acceptObject(*content.asObject);
case JSON_STRING:
return visitor.acceptString(content.asString);
case JSON_RAW:
return visitor.acceptRawJson(content.asRaw.data, content.asRaw.size);
case JSON_NEGATIVE_INTEGER:
return visitor.acceptNegativeInteger(content.asInteger);
case JSON_POSITIVE_INTEGER:
return visitor.acceptPositiveInteger(content.asInteger);
case JSON_BOOLEAN:
return visitor.acceptBoolean(content.asInteger != 0);
default:
return visitor.acceptNull();
}
}
};
} // namespace Internals
} // namespace ArduinoJson

View File

@ -1,23 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#pragma once
namespace ArduinoJson {
namespace Internals {
template <typename T>
struct JsonVariantDefault {
static T get() {
return T();
}
};
template <typename T>
struct JsonVariantDefault<const T> : JsonVariantDefault<T> {};
template <typename T>
struct JsonVariantDefault<T&> : JsonVariantDefault<T> {};
}
}

View File

@ -11,10 +11,10 @@ namespace Internals {
// Enumerated type to know the current type of a JsonVariant. // Enumerated type to know the current type of a JsonVariant.
// The value determines which member of JsonVariantContent is used. // The value determines which member of JsonVariantContent is used.
enum JsonVariantType { enum JsonVariantType {
JSON_UNDEFINED, // JsonVariant has not been initialized JSON_NULL, // JsonVariant has not been initialized
JSON_UNPARSED, // JsonVariant contains an unparsed string JSON_RAW, // JsonVariant contains a raw string that should not be escaped
JSON_STRING, // JsonVariant stores a const char* JSON_STRING, // JsonVariant stores a const char*
JSON_BOOLEAN, // JsonVariant stores a bool JSON_BOOLEAN, // JsonVariant stores a bool
JSON_POSITIVE_INTEGER, // JsonVariant stores an JsonUInt JSON_POSITIVE_INTEGER, // JsonVariant stores an JsonUInt
JSON_NEGATIVE_INTEGER, // JsonVariant stores an JsonUInt that must be negated JSON_NEGATIVE_INTEGER, // JsonVariant stores an JsonUInt that must be negated
JSON_ARRAY, // JsonVariant stores a pointer to a JsonArrayData JSON_ARRAY, // JsonVariant stores a pointer to a JsonArrayData

View File

@ -22,7 +22,7 @@ class List {
typedef ListIterator<T> iterator; typedef ListIterator<T> iterator;
typedef ListConstIterator<T> const_iterator; typedef ListConstIterator<T> const_iterator;
explicit List(JsonBuffer *buf) : _buffer(buf), _firstNode(NULL) {} List() : _firstNode(NULL) {}
// Returns the numbers of elements in the list. // Returns the numbers of elements in the list.
// For a JsonObjectData, it would return the number of key-value pairs // For a JsonObjectData, it would return the number of key-value pairs
@ -32,8 +32,8 @@ class List {
return nodeCount; return nodeCount;
} }
iterator add() { iterator add(JsonBuffer *buffer) {
node_type *newNode = new (_buffer) node_type(); node_type *newNode = new (buffer) node_type();
if (_firstNode) { if (_firstNode) {
node_type *lastNode = _firstNode; node_type *lastNode = _firstNode;
@ -71,11 +71,6 @@ class List {
} }
} }
JsonBuffer &buffer() const {
return *_buffer;
}
JsonBuffer *_buffer; // TODO!!
protected: protected:
void clear() { void clear() {
_firstNode = 0; _firstNode = 0;

View File

@ -1,53 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#pragma once
#include "../JsonVariant.hpp"
#include "../Memory/JsonBuffer.hpp"
#include "../Polyfills/type_traits.hpp"
#include "../Strings/StringTypes.hpp"
namespace ArduinoJson {
namespace Internals {
template <typename Source, typename Enable = void>
struct ValueSaver {
template <typename Destination>
static bool save(JsonBuffer*, Destination& destination, Source source) {
destination = source;
return true;
}
};
// We duplicate all strings except const char*
template <typename TString>
struct ValueSaver<
TString, typename enable_if<IsString<TString>::value &&
!is_same<const char*, TString>::value>::type> {
template <typename Destination>
static bool save(JsonBuffer* buffer, Destination& dest, TString source) {
const char* dup = makeString(source).save(buffer);
if (!dup) return false;
dest = dup;
return true;
}
};
// We duplicate all SerializedValue<T> except SerializedValue<const char*>
template <typename TString>
struct ValueSaver<
const SerializedValue<TString>&,
typename enable_if<!is_same<const char*, TString>::value>::type> {
template <typename Destination>
static bool save(JsonBuffer* buffer, Destination& dest,
const SerializedValue<TString>& source) {
const char* dup = makeString(source.data(), source.size()).save(buffer);
if (!dup) return false;
dest = SerializedValue<const char*>(dup, source.size());
return true;
}
};
} // namespace Internals
} // namespace ArduinoJson

View File

@ -35,7 +35,7 @@ deserialize(TDocument &doc, const TString &input) {
return makeDeserializer<TDeserializer>(&doc.buffer(), makeReader(input), return makeDeserializer<TDeserializer>(&doc.buffer(), makeReader(input),
makeStringStorage(doc.buffer(), input), makeStringStorage(doc.buffer(), input),
doc.nestingLimit) doc.nestingLimit)
.parse(doc.template to<JsonVariant>()); .parse(doc.template to<JsonVariantData>());
} }
// //
// DeserializationError deserialize(TDocument& doc, TChar* input); // DeserializationError deserialize(TDocument& doc, TChar* input);
@ -48,7 +48,7 @@ DeserializationError deserialize(TDocument &doc, TChar *input) {
return makeDeserializer<TDeserializer>(&doc.buffer(), makeReader(input), return makeDeserializer<TDeserializer>(&doc.buffer(), makeReader(input),
makeStringStorage(doc.buffer(), input), makeStringStorage(doc.buffer(), input),
doc.nestingLimit) doc.nestingLimit)
.parse(doc.template to<JsonVariant>()); .parse(doc.template to<JsonVariantData>());
} }
// //
// DeserializationError deserialize(TDocument& doc, TChar* input, size_t // DeserializationError deserialize(TDocument& doc, TChar* input, size_t
@ -63,7 +63,7 @@ DeserializationError deserialize(TDocument &doc, TChar *input,
return makeDeserializer<TDeserializer>( return makeDeserializer<TDeserializer>(
&doc.buffer(), makeReader(input, inputSize), &doc.buffer(), makeReader(input, inputSize),
makeStringStorage(doc.buffer(), input), doc.nestingLimit) makeStringStorage(doc.buffer(), input), doc.nestingLimit)
.parse(doc.template to<JsonVariant>()); .parse(doc.template to<JsonVariantData>());
} }
// //
// DeserializationError deserialize(TDocument& doc, TStream input); // DeserializationError deserialize(TDocument& doc, TStream input);
@ -76,7 +76,7 @@ DeserializationError deserialize(TDocument &doc, TStream &input) {
return makeDeserializer<TDeserializer>(&doc.buffer(), makeReader(input), return makeDeserializer<TDeserializer>(&doc.buffer(), makeReader(input),
makeStringStorage(doc.buffer(), input), makeStringStorage(doc.buffer(), input),
doc.nestingLimit) doc.nestingLimit)
.parse(doc.template to<JsonVariant>()); .parse(doc.template to<JsonVariantData>());
} }
} // namespace Internals } // namespace Internals
} // namespace ArduinoJson } // namespace ArduinoJson

View File

@ -12,24 +12,21 @@
namespace ArduinoJson { namespace ArduinoJson {
class DynamicJsonDocument { class DynamicJsonDocument {
Internals::DynamicJsonBuffer _buffer;
JsonVariant _root;
public: public:
uint8_t nestingLimit; uint8_t nestingLimit;
DynamicJsonDocument() : nestingLimit(ARDUINOJSON_DEFAULT_NESTING_LIMIT) {} DynamicJsonDocument() : nestingLimit(ARDUINOJSON_DEFAULT_NESTING_LIMIT) {}
DynamicJsonDocument(size_t capacity) DynamicJsonDocument(size_t capacity)
: _buffer(capacity), nestingLimit(ARDUINOJSON_DEFAULT_NESTING_LIMIT) {} : nestingLimit(ARDUINOJSON_DEFAULT_NESTING_LIMIT), _buffer(capacity) {}
template <typename T> template <typename T>
bool is() const { bool is() const {
return _root.is<T>(); return getVariant().is<T>();
} }
template <typename T> template <typename T>
typename Internals::JsonVariantAs<T>::type as() const { typename Internals::JsonVariantAs<T>::type as() const {
return _root.as<T>(); return getVariant().as<T>();
} }
// JsonObject to<JsonObject>() // JsonObject to<JsonObject>()
@ -39,7 +36,7 @@ class DynamicJsonDocument {
to() { to() {
clear(); clear();
JsonObject object(&_buffer); JsonObject object(&_buffer);
_root = object; getVariant().set(object);
return object; return object;
} }
@ -50,17 +47,27 @@ class DynamicJsonDocument {
to() { to() {
clear(); clear();
JsonArray array(&_buffer); JsonArray array(&_buffer);
_root = array; getVariant().set(array);
return array; return array;
} }
// JsonVariant& to<JsonVariant>() // JsonVariant to<JsonVariant>()
template <typename T> template <typename T>
typename Internals::enable_if<Internals::is_same<T, JsonVariant>::value, typename Internals::enable_if<Internals::is_same<T, JsonVariant>::value,
T&>::type JsonVariant>::type
to() { to() {
clear(); clear();
return _root; return getVariant();
}
// JsonVariantData& to<JsonVariantData>()
template <typename T>
typename Internals::enable_if<
Internals::is_same<T, Internals::JsonVariantData>::value,
Internals::JsonVariantData&>::type
to() {
clear();
return _rootData;
} }
Internals::DynamicJsonBuffer& buffer() { Internals::DynamicJsonBuffer& buffer() {
@ -69,7 +76,7 @@ class DynamicJsonDocument {
void clear() { void clear() {
_buffer.clear(); _buffer.clear();
_root = JsonVariant(); _rootData.setNull();
} }
size_t memoryUsage() const { size_t memoryUsage() const {
@ -78,7 +85,15 @@ class DynamicJsonDocument {
template <typename Visitor> template <typename Visitor>
void visit(Visitor& visitor) const { void visit(Visitor& visitor) const {
return _root.visit(visitor); return _rootData.visit(visitor);
} }
private:
JsonVariant getVariant() const {
return JsonVariant(&_buffer, &_rootData);
}
mutable Internals::DynamicJsonBuffer _buffer;
mutable Internals::JsonVariantData _rootData;
}; };
} // namespace ArduinoJson } // namespace ArduinoJson

View File

@ -7,6 +7,8 @@
#include "../Deserialization/deserialize.hpp" #include "../Deserialization/deserialize.hpp"
#include "../JsonVariant.hpp" #include "../JsonVariant.hpp"
#include "../Memory/JsonBuffer.hpp" #include "../Memory/JsonBuffer.hpp"
#include "../Numbers/isFloat.hpp"
#include "../Numbers/isInteger.hpp"
#include "../Polyfills/type_traits.hpp" #include "../Polyfills/type_traits.hpp"
#include "./EscapeSequence.hpp" #include "./EscapeSequence.hpp"
@ -23,7 +25,7 @@ class JsonDeserializer {
_stringStorage(stringStorage), _stringStorage(stringStorage),
_nestingLimit(nestingLimit), _nestingLimit(nestingLimit),
_loaded(false) {} _loaded(false) {}
DeserializationError parse(JsonVariant &variant) { DeserializationError parse(JsonVariantData &variant) {
DeserializationError err = skipSpacesAndComments(); DeserializationError err = skipSpacesAndComments();
if (err) return err; if (err) return err;
@ -63,12 +65,12 @@ class JsonDeserializer {
return true; return true;
} }
DeserializationError parseArray(JsonVariant &variant) { DeserializationError parseArray(JsonVariantData &variant) {
if (_nestingLimit == 0) return DeserializationError::TooDeep; if (_nestingLimit == 0) return DeserializationError::TooDeep;
JsonArray array(_buffer); JsonArrayData *array = new (_buffer) JsonArrayData;
if (array.isNull()) return DeserializationError::NoMemory; if (!array) return DeserializationError::NoMemory;
variant = array; variant.setArray(*array);
// Check opening braket // Check opening braket
if (!eat('[')) return DeserializationError::InvalidInput; if (!eat('[')) return DeserializationError::InvalidInput;
@ -82,13 +84,15 @@ class JsonDeserializer {
// Read each value // Read each value
for (;;) { for (;;) {
// Allocate slot in array
JsonVariantData *value = array->addSlot(_buffer);
if (!value) return DeserializationError::NoMemory;
// 1 - Parse value // 1 - Parse value
JsonVariant value;
_nestingLimit--; _nestingLimit--;
err = parse(value); err = parse(*value);
_nestingLimit++; _nestingLimit++;
if (err) return err; if (err) return err;
if (!array.add(value)) return DeserializationError::NoMemory;
// 2 - Skip spaces // 2 - Skip spaces
err = skipSpacesAndComments(); err = skipSpacesAndComments();
@ -100,12 +104,12 @@ class JsonDeserializer {
} }
} }
DeserializationError parseObject(JsonVariant &variant) { DeserializationError parseObject(JsonVariantData &variant) {
if (_nestingLimit == 0) return DeserializationError::TooDeep; if (_nestingLimit == 0) return DeserializationError::TooDeep;
JsonObject object(_buffer); JsonObjectData *object = new (_buffer) JsonObjectData;
if (object.isNull()) return DeserializationError::NoMemory; if (!object) return DeserializationError::NoMemory;
variant = object; variant.setObject(*object);
// Check opening brace // Check opening brace
if (!eat('{')) return DeserializationError::InvalidInput; if (!eat('{')) return DeserializationError::InvalidInput;
@ -129,13 +133,15 @@ class JsonDeserializer {
if (err) return err; // Colon if (err) return err; // Colon
if (!eat(':')) return DeserializationError::InvalidInput; if (!eat(':')) return DeserializationError::InvalidInput;
// Allocate slot in object
JsonVariantData *value = object->addSlot(_buffer, key);
if (!value) return DeserializationError::NoMemory;
// Parse value // Parse value
JsonVariant value;
_nestingLimit--; _nestingLimit--;
err = parse(value); err = parse(*value);
_nestingLimit++; _nestingLimit++;
if (err) return err; if (err) return err;
if (!object.set(key, value)) return DeserializationError::NoMemory;
// Skip spaces // Skip spaces
err = skipSpacesAndComments(); err = skipSpacesAndComments();
@ -151,7 +157,7 @@ class JsonDeserializer {
} }
} }
DeserializationError parseValue(JsonVariant &variant) { DeserializationError parseValue(JsonVariantData &variant) {
if (isQuote(current())) { if (isQuote(current())) {
return parseStringValue(variant); return parseStringValue(variant);
} else { } else {
@ -167,11 +173,11 @@ class JsonDeserializer {
} }
} }
DeserializationError parseStringValue(JsonVariant &variant) { DeserializationError parseStringValue(JsonVariantData &variant) {
const char *value; const char *value;
DeserializationError err = parseQuotedString(&value); DeserializationError err = parseQuotedString(&value);
if (err) return err; if (err) return err;
variant = value; variant.setString(value);
return DeserializationError::Ok; return DeserializationError::Ok;
} }
@ -229,7 +235,7 @@ class JsonDeserializer {
return DeserializationError::Ok; return DeserializationError::Ok;
} }
DeserializationError parseNumericValue(JsonVariant &result) { DeserializationError parseNumericValue(JsonVariantData &result) {
char buffer[64]; char buffer[64];
uint8_t n = 0; uint8_t n = 0;
@ -242,15 +248,15 @@ class JsonDeserializer {
buffer[n] = 0; buffer[n] = 0;
if (isInteger(buffer)) { if (isInteger(buffer)) {
result = parseInteger<JsonInteger>(buffer); result.setInteger(parseInteger<JsonInteger>(buffer));
} else if (isFloat(buffer)) { } else if (isFloat(buffer)) {
result = parseFloat<JsonFloat>(buffer); result.setFloat(parseFloat<JsonFloat>(buffer));
} else if (!strcmp(buffer, "true")) { } else if (!strcmp(buffer, "true")) {
result = true; result.setBoolean(true);
} else if (!strcmp(buffer, "false")) { } else if (!strcmp(buffer, "false")) {
result = false; result.setBoolean(false);
} else if (!strcmp(buffer, "null")) { } else if (!strcmp(buffer, "null")) {
result = static_cast<const char *>(0); result.setNull();
} else { } else {
return DeserializationError::InvalidInput; return DeserializationError::InvalidInput;
} }

View File

@ -20,10 +20,10 @@ class JsonSerializer {
_writer.writeFloat(value); _writer.writeFloat(value);
} }
void acceptArray(const JsonArray &array) { void acceptArray(const JsonArrayData &array) {
_writer.beginArray(); _writer.beginArray();
JsonArray::const_iterator it = array.begin(); JsonArrayData::const_iterator it = array.begin();
while (it != array.end()) { while (it != array.end()) {
it->visit(*this); it->visit(*this);
@ -36,10 +36,10 @@ class JsonSerializer {
_writer.endArray(); _writer.endArray();
} }
void acceptObject(const JsonObject &object) { void acceptObject(const JsonObjectData &object) {
_writer.beginObject(); _writer.beginObject();
JsonObject::const_iterator it = object.begin(); JsonObjectData::const_iterator it = object.begin();
while (it != object.end()) { while (it != object.end()) {
_writer.writeString(it->key); _writer.writeString(it->key);
_writer.writeColon(); _writer.writeColon();

View File

@ -4,7 +4,8 @@
#pragma once #pragma once
#include "./JsonArrayData.hpp" #include "JsonArrayData.hpp"
#include "JsonArrayIterator.hpp"
namespace ArduinoJson { namespace ArduinoJson {
@ -18,13 +19,13 @@ class JsonArray {
friend class JsonVariant; friend class JsonVariant;
public: public:
typedef Internals::JsonArrayData::iterator iterator; typedef JsonArrayIterator iterator;
typedef Internals::JsonArrayData::const_iterator const_iterator;
JsonArray() : _data(0) {} JsonArray() : _buffer(0), _data(0) {}
JsonArray(Internals::JsonArrayData* arr) : _data(arr) {} explicit JsonArray(Internals::JsonBuffer* buf, Internals::JsonArrayData* arr)
JsonArray(Internals::JsonBuffer* buf) : _buffer(buf), _data(arr) {}
: _data(new (buf) Internals::JsonArrayData(buf)) {} explicit JsonArray(Internals::JsonBuffer* buf)
: _buffer(buf), _data(new (buf) Internals::JsonArrayData()) {}
// Adds the specified value at the end of the array. // Adds the specified value at the end of the array.
// //
@ -43,24 +44,15 @@ class JsonArray {
return add_impl<T*>(value); return add_impl<T*>(value);
} }
iterator begin() { iterator begin() const {
if (!_data) return iterator(); if (!_data) return iterator();
return _data->begin(); return iterator(_buffer, _data->begin());
} }
const_iterator begin() const { iterator end() const {
if (!_data) return const_iterator();
return _data->begin();
}
iterator end() {
return iterator(); return iterator();
} }
const_iterator end() const {
return const_iterator();
}
// Imports a 1D array // Imports a 1D array
template <typename T, size_t N> template <typename T, size_t N>
bool copyFrom(T (&array)[N]) { bool copyFrom(T (&array)[N]) {
@ -100,8 +92,7 @@ class JsonArray {
template <typename T> template <typename T>
size_t copyTo(T* array, size_t len) const { size_t copyTo(T* array, size_t len) const {
size_t i = 0; size_t i = 0;
for (const_iterator it = begin(); it != end() && i < len; ++it) for (iterator it = begin(); it != end() && i < len; ++it) array[i++] = *it;
array[i++] = *it;
return i; return i;
} }
@ -110,7 +101,7 @@ class JsonArray {
void copyTo(T (&array)[N1][N2]) const { void copyTo(T (&array)[N1][N2]) const {
if (!_data) return; if (!_data) return;
size_t i = 0; size_t i = 0;
for (const_iterator it = begin(); it != end() && i < N1; ++it) { for (iterator it = begin(); it != end() && i < N1; ++it) {
it->as<JsonArray>().copyTo(array[i++]); it->as<JsonArray>().copyTo(array[i++]);
} }
} }
@ -129,21 +120,21 @@ class JsonArray {
// Gets the value at the specified index. // Gets the value at the specified index.
template <typename T> template <typename T>
typename Internals::JsonVariantAs<T>::type get(size_t index) const { typename Internals::JsonVariantAs<T>::type get(size_t index) const {
const_iterator it = begin() += index; iterator it = begin() += index;
return it != end() ? it->as<T>() : Internals::JsonVariantDefault<T>::get(); return it != end() ? it->as<T>() : T();
} }
// Check the type of the value at specified index. // Check the type of the value at specified index.
template <typename T> template <typename T>
bool is(size_t index) const { bool is(size_t index) const {
const_iterator it = begin() += index; iterator it = begin() += index;
return it != end() ? it->is<T>() : false; return it != end() ? it->is<T>() : false;
} }
// Removes element at specified position. // Removes element at specified position.
void remove(iterator it) { void remove(iterator it) {
if (!_data) return; if (!_data) return;
_data->remove(it); _data->remove(it.internal());
} }
// Removes element at specified index. // Removes element at specified index.
@ -182,7 +173,7 @@ class JsonArray {
template <typename Visitor> template <typename Visitor>
void visit(Visitor& visitor) const { void visit(Visitor& visitor) const {
if (_data) if (_data)
return visitor.acceptArray(*this); visitor.acceptArray(*_data);
else else
visitor.acceptNull(); visitor.acceptNull();
} }
@ -192,17 +183,18 @@ class JsonArray {
bool set_impl(size_t index, TValueRef value) { bool set_impl(size_t index, TValueRef value) {
iterator it = begin() += index; iterator it = begin() += index;
if (it == end()) return false; if (it == end()) return false;
return Internals::ValueSaver<TValueRef>::save(_data->_buffer, *it, value); return it->set(value);
} }
template <typename TValueRef> template <typename TValueRef>
bool add_impl(TValueRef value) { bool add_impl(TValueRef value) {
if (!_data) return false; if (!_data) return false;
iterator it = _data->add(); iterator it = iterator(_buffer, _data->add(_buffer));
if (it == end()) return false; if (it == end()) return false;
return Internals::ValueSaver<TValueRef>::save(_data->_buffer, *it, value); return it->set(value);
} }
Internals::JsonBuffer* _buffer;
Internals::JsonArrayData* _data; Internals::JsonArrayData* _data;
}; };
} // namespace ArduinoJson } // namespace ArduinoJson

View File

@ -4,9 +4,8 @@
#pragma once #pragma once
#include "Data/JsonVariantData.hpp"
#include "Data/List.hpp" #include "Data/List.hpp"
#include "Data/ValueSaver.hpp"
#include "JsonVariant.hpp"
#include "Memory/JsonBufferAllocated.hpp" #include "Memory/JsonBufferAllocated.hpp"
#include "Polyfills/type_traits.hpp" #include "Polyfills/type_traits.hpp"
@ -19,8 +18,11 @@
namespace ArduinoJson { namespace ArduinoJson {
namespace Internals { namespace Internals {
struct JsonArrayData : List<JsonVariant>, JsonBufferAllocated { struct JsonArrayData : List<JsonVariantData>, JsonBufferAllocated {
explicit JsonArrayData(JsonBuffer *buf) throw() : List<JsonVariant>(buf) {} JsonVariantData* addSlot(JsonBuffer* buffer) {
iterator it = add(buffer);
return it != end() ? &*it : 0;
}
}; };
} // namespace Internals } // namespace Internals
} // namespace ArduinoJson } // namespace ArduinoJson

View File

@ -11,14 +11,14 @@ namespace ArduinoJson {
inline JsonArray JsonArray::createNestedArray() { inline JsonArray JsonArray::createNestedArray() {
if (!_data) return JsonArray(); if (!_data) return JsonArray();
JsonArray array(_data->_buffer); JsonArray array(_buffer);
if (!array.isNull()) add(array); if (!array.isNull()) add(array);
return array; return array;
} }
inline JsonObject JsonArray::createNestedObject() { inline JsonObject JsonArray::createNestedObject() {
if (!_data) return JsonObject(); if (!_data) return JsonObject();
JsonObject object(_data->_buffer); JsonObject object(_buffer);
if (!object.isNull()) add(object); if (!object.isNull()) add(object);
return object; return object;
} }

View File

@ -0,0 +1,72 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#pragma once
#include "Data/ListIterator.hpp"
#include "JsonVariant.hpp"
namespace ArduinoJson {
class JsonVariantPtr {
public:
JsonVariantPtr(Internals::JsonBuffer *buffer,
Internals::JsonVariantData *data)
: _variant(buffer, data) {}
JsonVariant *operator->() {
return &_variant;
}
JsonVariant &operator*() {
return _variant;
}
private:
JsonVariant _variant;
};
class JsonArrayIterator {
typedef Internals::ListIterator<Internals::JsonVariantData> internal_iterator;
public:
JsonArrayIterator() {}
explicit JsonArrayIterator(Internals::JsonBuffer *buffer,
internal_iterator iterator)
: _iterator(iterator), _buffer(buffer) {}
JsonVariant operator*() const {
return JsonVariant(_buffer, &*_iterator);
}
JsonVariantPtr operator->() {
return JsonVariantPtr(_buffer, &*_iterator);
}
bool operator==(const JsonArrayIterator &other) const {
return _iterator == other._iterator;
}
bool operator!=(const JsonArrayIterator &other) const {
return _iterator != other._iterator;
}
JsonArrayIterator &operator++() {
++_iterator;
return *this;
}
JsonArrayIterator &operator+=(size_t distance) {
_iterator += distance;
return *this;
}
internal_iterator internal() {
return _iterator;
}
private:
internal_iterator _iterator;
Internals::JsonBuffer *_buffer;
};
} // namespace ArduinoJson

View File

@ -20,7 +20,7 @@ class JsonArraySubscript : public JsonVariantBase<JsonArraySubscript> {
: _array(array), _index(index) {} : _array(array), _index(index) {}
FORCE_INLINE JsonArraySubscript& operator=(const JsonArraySubscript& src) { FORCE_INLINE JsonArraySubscript& operator=(const JsonArraySubscript& src) {
_array.set(_index, src); _array.set(_index, src.as<JsonVariant>());
return *this; return *this;
} }

View File

@ -5,29 +5,27 @@
#pragma once #pragma once
#include "./JsonObjectData.hpp" #include "./JsonObjectData.hpp"
#include "./JsonObjectIterator.hpp"
namespace ArduinoJson { namespace ArduinoJson {
class JsonObject { class JsonObject {
friend class JsonVariant; friend class JsonVariant;
typedef Internals::JsonObjectData::iterator internal_iterator;
public: public:
typedef Internals::JsonObjectData::iterator iterator; typedef JsonObjectIterator iterator;
typedef Internals::JsonObjectData::const_iterator const_iterator;
JsonObject() : _data(0) {} JsonObject() : _buffer(0), _data(0) {}
JsonObject(Internals::JsonObjectData* object) : _data(object) {} explicit JsonObject(Internals::JsonBuffer* buf,
JsonObject(Internals::JsonBuffer* buf) Internals::JsonObjectData* object)
: _data(new (buf) Internals::JsonObjectData(buf)) {} : _buffer(buf), _data(object) {}
explicit JsonObject(Internals::JsonBuffer* buf)
: _buffer(buf), _data(new (buf) Internals::JsonObjectData()) {}
iterator begin() { iterator begin() const {
if (!_data) return iterator(); if (!_data) return iterator();
return _data->begin(); return iterator(_buffer, _data->begin());
}
const_iterator begin() const {
if (!_data) return const_iterator();
return _data->begin();
} }
// Tells weither the specified key is present and associated with a value. // Tells weither the specified key is present and associated with a value.
@ -46,14 +44,10 @@ class JsonObject {
return containsKey_impl<TString*>(key); return containsKey_impl<TString*>(key);
} }
iterator end() { iterator end() const {
return iterator(); return iterator();
} }
const_iterator end() const {
return const_iterator();
}
// Creates and adds a JsonArray. // Creates and adds a JsonArray.
// //
// JsonArray createNestedArray(TKey); // JsonArray createNestedArray(TKey);
@ -165,7 +159,7 @@ class JsonObject {
void remove(iterator it) { void remove(iterator it) {
if (!_data) return; if (!_data) return;
_data->remove(it); _data->remove(it.internal());
} }
// Removes the specified key and the associated value. // Removes the specified key and the associated value.
@ -232,15 +226,15 @@ class JsonObject {
template <typename Visitor> template <typename Visitor>
void visit(Visitor& visitor) const { void visit(Visitor& visitor) const {
if (_data) if (_data)
visitor.acceptObject(*this); visitor.acceptObject(*_data);
else else
return visitor.acceptNull(); visitor.acceptNull();
} }
private: private:
template <typename TStringRef> template <typename TStringRef>
bool containsKey_impl(TStringRef key) const { bool containsKey_impl(TStringRef key) const {
return findKey<TStringRef>(key) != end(); return findKey<TStringRef>(key) != _data->end();
} }
template <typename TStringRef> template <typename TStringRef>
@ -251,30 +245,32 @@ class JsonObject {
// Returns the list node that matches the specified key. // Returns the list node that matches the specified key.
template <typename TStringRef> template <typename TStringRef>
iterator findKey(TStringRef key) { internal_iterator findKey(TStringRef key) {
iterator it; if (!_data) return internal_iterator();
for (it = begin(); it != end(); ++it) { internal_iterator it;
for (it = _data->begin(); it != _data->end(); ++it) {
if (Internals::makeString(key).equals(it->key)) break; if (Internals::makeString(key).equals(it->key)) break;
} }
return it; return it;
} }
template <typename TStringRef> template <typename TStringRef>
const_iterator findKey(TStringRef key) const { internal_iterator findKey(TStringRef key) const {
return const_cast<JsonObject*>(this)->findKey<TStringRef>(key); return const_cast<JsonObject*>(this)->findKey<TStringRef>(key);
} }
template <typename TStringRef, typename TValue> template <typename TStringRef, typename TValue>
typename Internals::JsonVariantAs<TValue>::type get_impl( typename Internals::JsonVariantAs<TValue>::type get_impl(
TStringRef key) const { TStringRef key) const {
const_iterator it = findKey<TStringRef>(key); internal_iterator it = findKey<TStringRef>(key);
return it != end() ? it->value.as<TValue>() return it != _data->end() ? JsonVariant(_buffer, &it->value).as<TValue>()
: Internals::JsonVariantDefault<TValue>::get(); : TValue();
} }
template <typename TStringRef, typename TValue> template <typename TStringRef, typename TValue>
bool is_impl(TStringRef key) const { bool is_impl(TStringRef key) const {
const_iterator it = findKey<TStringRef>(key); internal_iterator it = findKey<TStringRef>(key);
return it != end() ? it->value.is<TValue>() : false; return it != _data->end() ? JsonVariant(_buffer, &it->value).is<TValue>()
: false;
} }
template <typename TStringRef> template <typename TStringRef>
@ -291,21 +287,33 @@ class JsonObject {
if (Internals::makeString(key).is_null()) return false; if (Internals::makeString(key).is_null()) return false;
// search a matching key // search a matching key
iterator it = findKey<TStringRef>(key); internal_iterator it = findKey<TStringRef>(key);
if (it == end()) { if (it == _data->end()) {
// add the key // add the key
it = _data->add(); // TODO: use JsonPairData directly, we don't need an iterator
if (it == end()) return false; it = _data->add(_buffer);
bool key_ok = if (it == _data->end()) return false;
Internals::ValueSaver<TStringRef>::save(_data->_buffer, it->key, key); if (!set_key(it, key)) return false;
if (!key_ok) return false;
} }
// save the value // save the value
return Internals::ValueSaver<TValueRef>::save(_data->_buffer, it->value, return JsonVariant(_buffer, &it->value).set(value);
value);
} }
Internals::JsonObjectData* _data; bool set_key(internal_iterator& it, const char* key) {
it->key = key;
return true;
}
template <typename T>
bool set_key(internal_iterator& it, const T& key) {
const char* dup = Internals::makeString(key).save(_buffer);
if (!dup) return false;
it->key = dup;
return true;
}
mutable Internals::JsonBuffer* _buffer;
mutable Internals::JsonObjectData* _data;
}; };
} // namespace ArduinoJson } // namespace ArduinoJson

View File

@ -5,7 +5,6 @@
#pragma once #pragma once
#include "Data/List.hpp" #include "Data/List.hpp"
#include "Data/ValueSaver.hpp"
#include "JsonPair.hpp" #include "JsonPair.hpp"
#include "Memory/JsonBufferAllocated.hpp" #include "Memory/JsonBufferAllocated.hpp"
#include "Polyfills/type_traits.hpp" #include "Polyfills/type_traits.hpp"
@ -19,8 +18,13 @@
namespace ArduinoJson { namespace ArduinoJson {
namespace Internals { namespace Internals {
struct JsonObjectData : List<JsonPair>, JsonBufferAllocated { struct JsonObjectData : List<JsonPairData>, JsonBufferAllocated {
explicit JsonObjectData(JsonBuffer* buf) throw() : List<JsonPair>(buf) {} JsonVariantData* addSlot(JsonBuffer* buffer, const char* key) {
iterator it = add(buffer);
if (it == end()) return 0;
it->key = key;
return &it->value;
}
}; };
} // namespace Internals } // namespace Internals
} // namespace ArduinoJson } // namespace ArduinoJson

View File

@ -22,7 +22,7 @@ inline JsonArray JsonObject::createNestedArray(TString* key) {
template <typename TStringRef> template <typename TStringRef>
inline JsonArray JsonObject::createNestedArray_impl(TStringRef key) { inline JsonArray JsonObject::createNestedArray_impl(TStringRef key) {
if (!_data) return JsonArray(); if (!_data) return JsonArray();
JsonArray array(_data->_buffer); JsonArray array(_buffer);
if (!array.isNull()) set(key, array); if (!array.isNull()) set(key, array);
return array; return array;
} }
@ -30,7 +30,7 @@ inline JsonArray JsonObject::createNestedArray_impl(TStringRef key) {
template <typename TStringRef> template <typename TStringRef>
inline JsonObject JsonObject::createNestedObject_impl(TStringRef key) { inline JsonObject JsonObject::createNestedObject_impl(TStringRef key) {
if (!_data) return JsonObject(); if (!_data) return JsonObject();
JsonObject object(_data->_buffer); JsonObject object(_buffer);
if (!object.isNull()) set(key, object); if (!object.isNull()) set(key, object);
return object; return object;
} }

View File

@ -0,0 +1,72 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#pragma once
#include "Data/ListIterator.hpp"
#include "JsonPair.hpp"
namespace ArduinoJson {
class JsonPairPtr {
public:
JsonPairPtr(Internals::JsonBuffer *buffer, Internals::JsonPairData *data)
: _pair(buffer, data) {}
const JsonPair *operator->() const {
return &_pair;
}
const JsonPair &operator*() const {
return _pair;
}
private:
JsonPair _pair;
};
// A read-write forward iterator for JsonArray
class JsonObjectIterator {
typedef Internals::ListIterator<Internals::JsonPairData> internal_iterator;
public:
JsonObjectIterator() {}
explicit JsonObjectIterator(Internals::JsonBuffer *buffer,
internal_iterator iterator)
: _buffer(buffer), _iterator(iterator) {}
JsonPair operator*() const {
return JsonPair(_buffer, &*_iterator);
}
JsonPairPtr operator->() {
return JsonPairPtr(_buffer, &*_iterator);
}
bool operator==(const JsonObjectIterator &other) const {
return _iterator == other._iterator;
}
bool operator!=(const JsonObjectIterator &other) const {
return _iterator != other._iterator;
}
JsonObjectIterator &operator++() {
++_iterator;
return *this;
}
JsonObjectIterator &operator+=(size_t distance) {
_iterator += distance;
return *this;
}
internal_iterator internal() {
return _iterator;
}
private:
Internals::JsonBuffer *_buffer;
internal_iterator _iterator;
};
} // namespace ArduinoJson

View File

@ -8,9 +8,30 @@
namespace ArduinoJson { namespace ArduinoJson {
// A key value pair for JsonObjectData. namespace Internals {
struct JsonPair {
struct JsonPairData {
const char* key; const char* key;
JsonVariant value; JsonVariantData value;
};
} // namespace Internals
// A key value pair for JsonObjectData.
class JsonPair {
public:
JsonPair(Internals::JsonBuffer* buffer, Internals::JsonPairData* data)
: _key(data->key), _value(buffer, &data->value) {}
const char* key() const {
return _key;
}
JsonVariant value() const {
return _value;
}
private:
const char* _key;
JsonVariant _value;
}; };
} // namespace ArduinoJson } // namespace ArduinoJson

View File

@ -7,10 +7,10 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> // for uint8_t #include <stdint.h> // for uint8_t
#include "Data/JsonVariantContent.hpp" #include "Data/JsonVariantData.hpp"
#include "Data/JsonVariantDefault.hpp" #include "JsonVariant.hpp"
#include "Data/JsonVariantType.hpp"
#include "JsonVariantBase.hpp" #include "JsonVariantBase.hpp"
#include "Memory/JsonBuffer.hpp"
#include "Polyfills/type_traits.hpp" #include "Polyfills/type_traits.hpp"
#include "Serialization/DynamicStringWriter.hpp" #include "Serialization/DynamicStringWriter.hpp"
#include "SerializedValue.hpp" #include "SerializedValue.hpp"
@ -30,84 +30,135 @@ class JsonObject;
// - a reference to a JsonArray or JsonObject // - a reference to a JsonArray or JsonObject
class JsonVariant : public Internals::JsonVariantBase<JsonVariant> { class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
public: public:
// Intenal use only
explicit JsonVariant(Internals::JsonBuffer *buffer,
Internals::JsonVariantData *data)
: _buffer(buffer), _data(data) {}
// Creates an uninitialized JsonVariant // Creates an uninitialized JsonVariant
JsonVariant() : _type(Internals::JSON_UNDEFINED) {} JsonVariant() : _buffer(0), _data(0) {}
// Create a JsonVariant containing a boolean value. // set(bool value)
// It will be serialized as "true" or "false" in JSON. bool set(bool value) {
JsonVariant(bool value) { if (!_data) return false;
using namespace Internals; _data->setBoolean(value);
_type = JSON_BOOLEAN; return true;
_content.asInteger = static_cast<JsonUInt>(value);
} }
// Create a JsonVariant containing a floating point value. // set(double value);
// JsonVariant(double value); // set(float value);
// JsonVariant(float value);
template <typename T> template <typename T>
JsonVariant(T value, bool set(T value, typename Internals::enable_if<
typename Internals::enable_if< Internals::is_floating_point<T>::value>::type * = 0) {
Internals::is_floating_point<T>::value>::type * = 0) { if (!_data) return false;
using namespace Internals; _data->setFloat(static_cast<Internals::JsonFloat>(value));
_type = JSON_FLOAT; return true;
_content.asFloat = static_cast<JsonFloat>(value);
} }
// Create a JsonVariant containing an integer value. // set(char)
// JsonVariant(char) // set(signed short)
// JsonVariant(signed short) // set(signed int)
// JsonVariant(signed int) // set(signed long)
// JsonVariant(signed long) // set(signed char)
// JsonVariant(signed char)
template <typename T> template <typename T>
JsonVariant( bool set(T value,
T value, typename Internals::enable_if<Internals::is_integral<T>::value &&
typename Internals::enable_if<Internals::is_integral<T>::value && Internals::is_signed<T>::value>::type
Internals::is_signed<T>::value>::type * = * = 0) {
0) { if (!_data) return false;
using namespace Internals; if (value >= 0)
if (value >= 0) { _data->setPostiveInteger(static_cast<Internals::JsonUInt>(value));
_type = JSON_POSITIVE_INTEGER; else
_content.asInteger = static_cast<JsonUInt>(value); _data->setNegativeInteger(~static_cast<Internals::JsonUInt>(value) + 1);
return true;
}
// set(unsigned short)
// set(unsigned int)
// set(unsigned long)
template <typename T>
bool set(T value,
typename Internals::enable_if<Internals::is_integral<T>::value &&
Internals::is_unsigned<T>::value>::type
* = 0) {
if (!_data) return false;
_data->setPostiveInteger(static_cast<Internals::JsonUInt>(value));
return true;
}
// set(SerializedValue<const char *>)
bool set(Internals::SerializedValue<const char *> value) {
if (!_data) return false;
_data->setRaw(value.data(), value.size());
return true;
}
// set(SerializedValue<std::string>)
// set(SerializedValue<String>)
// set(SerializedValue<const __FlashStringHelper*>)
template <typename T>
bool set(Internals::SerializedValue<T> value,
typename Internals::enable_if<
!Internals::is_same<const char *, T>::value>::type * = 0) {
if (!_data) return false;
const char *dup =
Internals::makeString(value.data(), value.size()).save(_buffer);
if (dup)
_data->setRaw(dup, value.size());
else
_data->setNull();
return true;
}
// set(const std::string&)
// set(const String&)
template <typename T>
bool set(const T &value,
typename Internals::enable_if<Internals::IsString<T>::value>::type
* = 0) {
if (!_data) return false;
const char *dup = Internals::makeString(value).save(_buffer);
if (dup) {
_data->setString(dup);
return true;
} else { } else {
_type = JSON_NEGATIVE_INTEGER; _data->setNull();
_content.asInteger = ~static_cast<JsonUInt>(value) + 1; return false;
} }
} }
// JsonVariant(unsigned short)
// JsonVariant(unsigned int) // set(const char*);
// JsonVariant(unsigned long) // set(const char[n]); // VLA
template <typename T> bool set(const char *value) {
JsonVariant( if (!_data) return false;
T value, _data->setString(value);
typename Internals::enable_if<Internals::is_integral<T>::value && return true;
Internals::is_unsigned<T>::value>::type * = }
0) { // set(const unsigned char*);
using namespace Internals; // set(const unsigned char[n]); // VLA
_type = JSON_POSITIVE_INTEGER; bool set(const unsigned char *value) {
_content.asInteger = static_cast<JsonUInt>(value); return set(reinterpret_cast<const char *>(value));
}
// set(const signed char*);
// set(const signed char[n]); // VLA
bool set(const signed char *value) {
return set(reinterpret_cast<const char *>(value));
} }
// Create a JsonVariant containing a string. bool set(const JsonVariant &value) {
// JsonVariant(const char*); if (!_data) return false;
// JsonVariant(const signed char*); if (value._data)
// JsonVariant(const unsigned char*); *_data = *value._data;
template <typename TChar> else
JsonVariant(const TChar *value, _data->setNull();
typename Internals::enable_if<sizeof(TChar) == 1>::type * = 0) { return true;
_type = Internals::JSON_STRING;
_content.asString = reinterpret_cast<const char *>(value);
} }
// Create a JsonVariant containing an unparsed string bool set(const JsonArray &array);
JsonVariant(Internals::SerializedValue<const char *> value) { bool set(const Internals::JsonArraySubscript &);
_type = Internals::JSON_UNPARSED; bool set(const JsonObject &object);
_content.asRaw.data = value.data(); template <typename TString>
_content.asRaw.size = value.size(); bool set(const Internals::JsonObjectSubscript<TString> &);
}
JsonVariant(JsonArray array);
JsonVariant(JsonObject object);
// Get the variant as the specified type. // Get the variant as the specified type.
// //
@ -123,14 +174,14 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
template <typename T> template <typename T>
const typename Internals::enable_if<Internals::is_integral<T>::value, T>::type const typename Internals::enable_if<Internals::is_integral<T>::value, T>::type
as() const { as() const {
return variantAsInteger<T>(); return _data ? _data->asInteger<T>() : T();
} }
// bool as<bool>() const // bool as<bool>() const
template <typename T> template <typename T>
const typename Internals::enable_if<Internals::is_same<T, bool>::value, const typename Internals::enable_if<Internals::is_same<T, bool>::value,
T>::type T>::type
as() const { as() const {
return variantAsInteger<int>() != 0; return _data && _data->asInteger<int>() != 0;
} }
// //
// double as<double>() const; // double as<double>() const;
@ -139,7 +190,7 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
const typename Internals::enable_if<Internals::is_floating_point<T>::value, const typename Internals::enable_if<Internals::is_floating_point<T>::value,
T>::type T>::type
as() const { as() const {
return variantAsFloat<T>(); return _data ? _data->asFloat<T>() : 0;
} }
// //
// const char* as<const char*>() const; // const char* as<const char*>() const;
@ -149,7 +200,7 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
Internals::is_same<T, char *>::value, Internals::is_same<T, char *>::value,
const char *>::type const char *>::type
as() const { as() const {
return variantAsString(); return _data ? _data->asString() : 0;
} }
// //
// std::string as<std::string>() const; // std::string as<std::string>() const;
@ -157,7 +208,7 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
template <typename T> template <typename T>
typename Internals::enable_if<Internals::IsWriteableString<T>::value, T>::type typename Internals::enable_if<Internals::IsWriteableString<T>::value, T>::type
as() const { as() const {
const char *cstr = variantAsString(); const char *cstr = _data ? _data->asString() : 0;
if (cstr) return T(cstr); if (cstr) return T(cstr);
T s; T s;
serializeJson(*this, s); serializeJson(*this, s);
@ -205,7 +256,7 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
template <typename T> template <typename T>
typename Internals::enable_if<Internals::is_integral<T>::value, bool>::type typename Internals::enable_if<Internals::is_integral<T>::value, bool>::type
is() const { is() const {
return variantIsInteger(); return _data && _data->isInteger();
} }
// //
// bool is<double>() const; // bool is<double>() const;
@ -214,14 +265,14 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
typename Internals::enable_if<Internals::is_floating_point<T>::value, typename Internals::enable_if<Internals::is_floating_point<T>::value,
bool>::type bool>::type
is() const { is() const {
return variantIsFloat(); return _data && _data->isFloat();
} }
// //
// bool is<bool>() const // bool is<bool>() const
template <typename T> template <typename T>
typename Internals::enable_if<Internals::is_same<T, bool>::value, bool>::type typename Internals::enable_if<Internals::is_same<T, bool>::value, bool>::type
is() const { is() const {
return variantIsBoolean(); return _data && _data->isBoolean();
} }
// //
// bool is<const char*>() const; // bool is<const char*>() const;
@ -231,7 +282,7 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
Internals::is_same<T, char *>::value, Internals::is_same<T, char *>::value,
bool>::type bool>::type
is() const { is() const {
return variantIsString(); return _data && _data->isString();
} }
// //
// bool is<JsonArray> const; // bool is<JsonArray> const;
@ -242,7 +293,7 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
JsonArray>::value, JsonArray>::value,
bool>::type bool>::type
is() const { is() const {
return variantIsArray(); return _data && _data->isArray();
} }
// //
// bool is<JsonObject> const; // bool is<JsonObject> const;
@ -253,72 +304,24 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
JsonObject>::value, JsonObject>::value,
bool>::type bool>::type
is() const { is() const {
return variantIsObject(); return _data && _data->isObject();
} }
// Returns true if the variant has a value // Returns true if the variant has a value
bool isNull() const { bool isNull() const {
return _type == Internals::JSON_UNDEFINED; return _data == 0 || _data->isNull();
} }
template <typename Visitor> template <typename Visitor>
void visit(Visitor &visitor) const { void visit(Visitor &visitor) const {
using namespace Internals; if (_data)
switch (_type) { _data->visit(visitor);
case JSON_FLOAT: else
return visitor.acceptFloat(_content.asFloat); visitor.acceptNull();
case JSON_ARRAY:
return visitor.acceptArray(_content.asArray);
case JSON_OBJECT:
return visitor.acceptObject(_content.asObject);
case JSON_STRING:
return visitor.acceptString(_content.asString);
case JSON_UNPARSED:
return visitor.acceptRawJson(_content.asRaw.data, _content.asRaw.size);
case JSON_NEGATIVE_INTEGER:
return visitor.acceptNegativeInteger(_content.asInteger);
case JSON_POSITIVE_INTEGER:
return visitor.acceptPositiveInteger(_content.asInteger);
case JSON_BOOLEAN:
return visitor.acceptBoolean(_content.asInteger != 0);
default: // JSON_UNDEFINED
return visitor.acceptNull();
}
} }
private: private:
JsonArray variantAsArray() const; Internals::JsonBuffer *_buffer;
JsonObject variantAsObject() const; Internals::JsonVariantData *_data;
const char *variantAsString() const; }; // namespace ArduinoJson
template <typename T>
T variantAsFloat() const;
template <typename T>
T variantAsInteger() const;
bool variantIsBoolean() const;
bool variantIsFloat() const;
bool variantIsInteger() const;
bool variantIsArray() const {
return _type == Internals::JSON_ARRAY;
}
bool variantIsObject() const {
return _type == Internals::JSON_OBJECT;
}
bool variantIsString() const {
return _type == Internals::JSON_STRING;
}
// The current type of the variant
Internals::JsonVariantType _type;
// The various alternatives for the value of the variant.
Internals::JsonVariantContent _content;
};
} // namespace ArduinoJson } // namespace ArduinoJson

View File

@ -5,6 +5,8 @@
#pragma once #pragma once
#include "Data/IsVariant.hpp" #include "Data/IsVariant.hpp"
#include "Data/JsonFloat.hpp"
#include "Data/JsonInteger.hpp"
#include "Polyfills/type_traits.hpp" #include "Polyfills/type_traits.hpp"
#include "Strings/StringTypes.hpp" #include "Strings/StringTypes.hpp"

View File

@ -8,8 +8,6 @@
#include "JsonArrayData.hpp" #include "JsonArrayData.hpp"
#include "JsonObjectData.hpp" #include "JsonObjectData.hpp"
#include "JsonVariant.hpp" #include "JsonVariant.hpp"
#include "Numbers/isFloat.hpp"
#include "Numbers/isInteger.hpp"
#include "Numbers/parseFloat.hpp" #include "Numbers/parseFloat.hpp"
#include "Numbers/parseInteger.hpp" #include "Numbers/parseInteger.hpp"
@ -17,22 +15,32 @@
namespace ArduinoJson { namespace ArduinoJson {
inline JsonVariant::JsonVariant(JsonArray array) { inline bool JsonVariant::set(const JsonArray& array) {
if (!array.isNull()) { if (!_data) return false;
_type = Internals::JSON_ARRAY; if (array._data)
_content.asArray = array._data; _data->setArray(*array._data);
} else { else
_type = Internals::JSON_UNDEFINED; _data->setNull();
} return true;
} }
inline JsonVariant::JsonVariant(JsonObject object) { inline bool JsonVariant::set(const Internals::JsonArraySubscript& value) {
if (!object.isNull()) { return set(value.as<JsonVariant>());
_type = Internals::JSON_OBJECT; }
_content.asObject = object._data;
} else { inline bool JsonVariant::set(const JsonObject& object) {
_type = Internals::JSON_UNDEFINED; if (!_data) return false;
} if (object._data)
_data->setObject(*object._data);
else
_data->setNull();
return true;
}
template <typename TString>
inline bool JsonVariant::set(
const Internals::JsonObjectSubscript<TString>& value) {
return set(value.template as<JsonVariant>());
} }
template <typename T> template <typename T>
@ -41,7 +49,7 @@ inline typename Internals::enable_if<
JsonArray>::value, JsonArray>::value,
JsonArray>::type JsonArray>::type
JsonVariant::as() const { JsonVariant::as() const {
return variantAsArray(); return _data ? JsonArray(_buffer, _data->asArray()) : JsonArray();
} }
template <typename T> template <typename T>
@ -50,78 +58,6 @@ inline typename Internals::enable_if<
JsonObject>::value, JsonObject>::value,
T>::type T>::type
JsonVariant::as() const { JsonVariant::as() const {
return variantAsObject(); return _data ? JsonObject(_buffer, _data->asObject()) : JsonObject();
} }
inline JsonArray JsonVariant::variantAsArray() const {
if (_type == Internals::JSON_ARRAY) return _content.asArray;
return JsonArray();
}
inline JsonObject JsonVariant::variantAsObject() const {
if (_type == Internals::JSON_OBJECT) return _content.asObject;
return JsonObject();
}
template <typename T>
inline T JsonVariant::variantAsInteger() const {
using namespace Internals;
switch (_type) {
case JSON_UNDEFINED:
case JSON_UNPARSED:
return 0;
case JSON_POSITIVE_INTEGER:
case JSON_BOOLEAN:
return T(_content.asInteger);
case JSON_NEGATIVE_INTEGER:
return T(~_content.asInteger + 1);
case JSON_STRING:
return parseInteger<T>(_content.asString);
default:
return T(_content.asFloat);
}
}
inline const char *JsonVariant::variantAsString() const {
using namespace Internals;
return _type == JSON_STRING ? _content.asString : NULL;
}
template <typename T>
inline T JsonVariant::variantAsFloat() const {
using namespace Internals;
switch (_type) {
case JSON_UNDEFINED:
case JSON_UNPARSED:
return 0;
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_STRING:
return parseFloat<T>(_content.asString);
default:
return static_cast<T>(_content.asFloat);
}
}
inline bool JsonVariant::variantIsBoolean() const {
using namespace Internals;
return _type == JSON_BOOLEAN;
}
inline bool JsonVariant::variantIsInteger() const {
using namespace Internals;
return _type == JSON_POSITIVE_INTEGER || _type == JSON_NEGATIVE_INTEGER;
}
inline bool JsonVariant::variantIsFloat() const {
using namespace Internals;
return _type == JSON_FLOAT || _type == JSON_POSITIVE_INTEGER ||
_type == JSON_NEGATIVE_INTEGER;
}
} // namespace ArduinoJson } // namespace ArduinoJson

View File

@ -16,7 +16,7 @@ class JsonBufferAllocated {
return jsonBuffer->alloc(n); return jsonBuffer->alloc(n);
} }
void operator delete(void *, JsonBuffer *)throw(); void operator delete(void *, JsonBuffer *)throw() {}
}; };
} // namespace Internals } // namespace Internals
} // namespace ArduinoJson } // namespace ArduinoJson

View File

@ -24,17 +24,17 @@ class MsgPackDeserializer {
_stringStorage(stringStorage), _stringStorage(stringStorage),
_nestingLimit(nestingLimit) {} _nestingLimit(nestingLimit) {}
DeserializationError parse(JsonVariant &variant) { DeserializationError parse(JsonVariantData &variant) {
uint8_t code; uint8_t code;
if (!readByte(code)) return DeserializationError::IncompleteInput; if (!readByte(code)) return DeserializationError::IncompleteInput;
if ((code & 0x80) == 0) { if ((code & 0x80) == 0) {
variant = code; variant.setInteger(code);
return DeserializationError::Ok; return DeserializationError::Ok;
} }
if ((code & 0xe0) == 0xe0) { if ((code & 0xe0) == 0xe0) {
variant = static_cast<int8_t>(code); variant.setInteger(static_cast<int8_t>(code));
return DeserializationError::Ok; return DeserializationError::Ok;
} }
@ -48,15 +48,15 @@ class MsgPackDeserializer {
switch (code) { switch (code) {
case 0xc0: case 0xc0:
variant = static_cast<char *>(0); variant.setNull();
return DeserializationError::Ok; return DeserializationError::Ok;
case 0xc2: case 0xc2:
variant = false; variant.setBoolean(false);
return DeserializationError::Ok; return DeserializationError::Ok;
case 0xc3: case 0xc3:
variant = true; variant.setBoolean(true);
return DeserializationError::Ok; return DeserializationError::Ok;
case 0xcc: case 0xcc:
@ -171,54 +171,54 @@ class MsgPackDeserializer {
} }
template <typename T> template <typename T>
DeserializationError readInteger(JsonVariant &variant) { DeserializationError readInteger(JsonVariantData &variant) {
T value; T value;
if (!readInteger(value)) return DeserializationError::IncompleteInput; if (!readInteger(value)) return DeserializationError::IncompleteInput;
variant = value; variant.setInteger(value);
return DeserializationError::Ok; return DeserializationError::Ok;
} }
template <typename T> template <typename T>
typename enable_if<sizeof(T) == 4, DeserializationError>::type readFloat( typename enable_if<sizeof(T) == 4, DeserializationError>::type readFloat(
JsonVariant &variant) { JsonVariantData &variant) {
T value; T value;
if (!readBytes(value)) return DeserializationError::IncompleteInput; if (!readBytes(value)) return DeserializationError::IncompleteInput;
fixEndianess(value); fixEndianess(value);
variant = value; variant.setFloat(value);
return DeserializationError::Ok; return DeserializationError::Ok;
} }
template <typename T> template <typename T>
typename enable_if<sizeof(T) == 8, DeserializationError>::type readDouble( typename enable_if<sizeof(T) == 8, DeserializationError>::type readDouble(
JsonVariant &variant) { JsonVariantData &variant) {
T value; T value;
if (!readBytes(value)) return DeserializationError::IncompleteInput; if (!readBytes(value)) return DeserializationError::IncompleteInput;
fixEndianess(value); fixEndianess(value);
variant = value; variant.setFloat(value);
return DeserializationError::Ok; return DeserializationError::Ok;
} }
template <typename T> template <typename T>
typename enable_if<sizeof(T) == 4, DeserializationError>::type readDouble( typename enable_if<sizeof(T) == 4, DeserializationError>::type readDouble(
JsonVariant &variant) { JsonVariantData &variant) {
uint8_t i[8]; // input is 8 bytes uint8_t i[8]; // input is 8 bytes
T value; // output is 4 bytes T value; // output is 4 bytes
uint8_t *o = reinterpret_cast<uint8_t *>(&value); uint8_t *o = reinterpret_cast<uint8_t *>(&value);
if (!readBytes(i, 8)) return DeserializationError::IncompleteInput; if (!readBytes(i, 8)) return DeserializationError::IncompleteInput;
doubleToFloat(i, o); doubleToFloat(i, o);
fixEndianess(value); fixEndianess(value);
variant = value; variant.setFloat(value);
return DeserializationError::Ok; return DeserializationError::Ok;
} }
template <typename T> template <typename T>
DeserializationError readString(JsonVariant &variant) { DeserializationError readString(JsonVariantData &variant) {
T size; T size;
if (!readInteger(size)) return DeserializationError::IncompleteInput; if (!readInteger(size)) return DeserializationError::IncompleteInput;
return readString(variant, size); return readString(variant, size);
} }
DeserializationError readString(JsonVariant &variant, size_t n) { DeserializationError readString(JsonVariantData &variant, size_t n) {
typename remove_reference<TStringStorage>::type::String str = typename remove_reference<TStringStorage>::type::String str =
_stringStorage.startString(); _stringStorage.startString();
for (; n; --n) { for (; n; --n) {
@ -228,64 +228,68 @@ class MsgPackDeserializer {
} }
const char *s = str.c_str(); const char *s = str.c_str();
if (s == NULL) return DeserializationError::NoMemory; if (s == NULL) return DeserializationError::NoMemory;
variant = s; variant.setString(s);
return DeserializationError::Ok; return DeserializationError::Ok;
} }
template <typename TSize> template <typename TSize>
DeserializationError readArray(JsonVariant &variant) { DeserializationError readArray(JsonVariantData &variant) {
TSize size; TSize size;
if (!readInteger(size)) return DeserializationError::IncompleteInput; if (!readInteger(size)) return DeserializationError::IncompleteInput;
return readArray(variant, size); return readArray(variant, size);
} }
DeserializationError readArray(JsonVariant &variant, size_t n) { DeserializationError readArray(JsonVariantData &variant, size_t n) {
JsonArray array(_buffer); JsonArrayData *array = new (_buffer) JsonArrayData;
if (array.isNull()) return DeserializationError::NoMemory; if (!array) return DeserializationError::NoMemory;
variant = array;
return readArray(array, n); variant.setArray(*array);
return readArray(*array, n);
} }
DeserializationError readArray(JsonArray array, size_t n) { DeserializationError readArray(JsonArrayData &array, size_t n) {
if (_nestingLimit == 0) return DeserializationError::TooDeep; if (_nestingLimit == 0) return DeserializationError::TooDeep;
--_nestingLimit; --_nestingLimit;
for (; n; --n) { for (; n; --n) {
JsonVariant variant; JsonVariantData *value = array.addSlot(_buffer);
DeserializationError err = parse(variant); if (!value) return DeserializationError::NoMemory;
DeserializationError err = parse(*value);
if (err) return err; if (err) return err;
if (!array.add(variant)) return DeserializationError::NoMemory;
} }
++_nestingLimit; ++_nestingLimit;
return DeserializationError::Ok; return DeserializationError::Ok;
} }
template <typename TSize> template <typename TSize>
DeserializationError readObject(JsonVariant &variant) { DeserializationError readObject(JsonVariantData &variant) {
TSize size; TSize size;
if (!readInteger(size)) return DeserializationError::IncompleteInput; if (!readInteger(size)) return DeserializationError::IncompleteInput;
return readObject(variant, size); return readObject(variant, size);
} }
DeserializationError readObject(JsonVariant &variant, size_t n) { DeserializationError readObject(JsonVariantData &variant, size_t n) {
JsonObject object(_buffer); JsonObjectData *object = new (_buffer) JsonObjectData;
if (object.isNull()) return DeserializationError::NoMemory; if (!object) return DeserializationError::NoMemory;
variant = object; variant.setObject(*object);
return readObject(object, n);
return readObject(*object, n);
} }
DeserializationError readObject(JsonObject object, size_t n) { DeserializationError readObject(JsonObjectData &object, size_t n) {
if (_nestingLimit == 0) return DeserializationError::TooDeep; if (_nestingLimit == 0) return DeserializationError::TooDeep;
--_nestingLimit; --_nestingLimit;
for (; n; --n) { for (; n; --n) {
DeserializationError err; JsonVariantData key;
JsonVariant variant; DeserializationError err = parse(key);
err = parse(variant);
if (err) return err; if (err) return err;
const char *key = variant.as<char *>(); if (!key.isString()) return DeserializationError::NotSupported;
if (!key) return DeserializationError::NotSupported;
err = parse(variant); JsonVariantData *value = object.addSlot(_buffer, key.asString());
if (!value) return DeserializationError::NoMemory;
err = parse(*value);
if (err) return err; if (err) return err;
if (!object.set(key, variant)) return DeserializationError::NoMemory;
} }
++_nestingLimit; ++_nestingLimit;
return DeserializationError::Ok; return DeserializationError::Ok;

View File

@ -36,7 +36,7 @@ class MsgPackSerializer {
} }
} }
void acceptArray(const JsonArray& array) { void acceptArray(const JsonArrayData& array) {
size_t n = array.size(); size_t n = array.size();
if (n < 0x10) { if (n < 0x10) {
writeByte(uint8_t(0x90 + array.size())); writeByte(uint8_t(0x90 + array.size()));
@ -47,13 +47,13 @@ class MsgPackSerializer {
writeByte(0xDD); writeByte(0xDD);
writeInteger(uint32_t(n)); writeInteger(uint32_t(n));
} }
for (JsonArray::const_iterator it = array.begin(); it != array.end(); for (JsonArrayData::const_iterator it = array.begin(); it != array.end();
++it) { ++it) {
it->visit(*this); it->visit(*this);
} }
} }
void acceptObject(const JsonObject& object) { void acceptObject(const JsonObjectData& object) {
size_t n = object.size(); size_t n = object.size();
if (n < 0x10) { if (n < 0x10) {
writeByte(uint8_t(0x80 + n)); writeByte(uint8_t(0x80 + n));
@ -64,7 +64,7 @@ class MsgPackSerializer {
writeByte(0xDF); writeByte(0xDF);
writeInteger(uint32_t(n)); writeInteger(uint32_t(n));
} }
for (JsonObject::const_iterator it = object.begin(); it != object.end(); for (JsonObjectData::const_iterator it = object.begin(); it != object.end();
++it) { ++it) {
acceptString(it->key); acceptString(it->key);
it->value.visit(*this); it->value.visit(*this);

View File

@ -4,6 +4,7 @@
#pragma once #pragma once
#include "../../Configuration.hpp"
#include "is_same.hpp" #include "is_same.hpp"
namespace ArduinoJson { namespace ArduinoJson {

View File

@ -9,11 +9,8 @@
namespace ArduinoJson { namespace ArduinoJson {
template <size_t CAPACITY = sizeof(JsonVariant)> template <size_t CAPACITY>
class StaticJsonDocument { class StaticJsonDocument {
Internals::StaticJsonBuffer<CAPACITY> _buffer;
JsonVariant _root;
public: public:
uint8_t nestingLimit; uint8_t nestingLimit;
@ -25,12 +22,12 @@ class StaticJsonDocument {
template <typename T> template <typename T>
bool is() const { bool is() const {
return _root.is<T>(); return getVariant().template is<T>();
} }
template <typename T> template <typename T>
typename Internals::JsonVariantAs<T>::type as() const { typename Internals::JsonVariantAs<T>::type as() const {
return _root.as<T>(); return getVariant().template as<T>();
} }
// JsonObject to<JsonObject>() // JsonObject to<JsonObject>()
@ -40,7 +37,7 @@ class StaticJsonDocument {
to() { to() {
clear(); clear();
JsonObject object(&_buffer); JsonObject object(&_buffer);
_root = object; getVariant().set(object);
return object; return object;
} }
@ -51,22 +48,32 @@ class StaticJsonDocument {
to() { to() {
clear(); clear();
JsonArray array(&_buffer); JsonArray array(&_buffer);
_root = array; getVariant().set(array);
return array; return array;
} }
// JsonVariant to<JsonVariant>() // JsonVariant to<JsonVariant>()
template <typename T> template <typename T>
typename Internals::enable_if<Internals::is_same<T, JsonVariant>::value, typename Internals::enable_if<Internals::is_same<T, JsonVariant>::value,
T&>::type JsonVariant>::type
to() { to() {
clear(); clear();
return _root; return getVariant();
}
// JsonVariantData& to<JsonVariantData>()
template <typename T>
typename Internals::enable_if<
Internals::is_same<T, Internals::JsonVariantData>::value,
Internals::JsonVariantData&>::type
to() {
clear();
return _rootData;
} }
void clear() { void clear() {
_buffer.clear(); _buffer.clear();
_root = JsonVariant(); _rootData.setNull();
} }
size_t memoryUsage() const { size_t memoryUsage() const {
@ -75,8 +82,16 @@ class StaticJsonDocument {
template <typename Visitor> template <typename Visitor>
void visit(Visitor& visitor) const { void visit(Visitor& visitor) const {
return _root.visit(visitor); return getVariant().visit(visitor);
} }
private:
JsonVariant getVariant() const {
return JsonVariant(&_buffer, &_rootData);
}
mutable Internals::StaticJsonBuffer<CAPACITY> _buffer;
mutable Internals::JsonVariantData _rootData;
}; };
} // namespace ArduinoJson } // namespace ArduinoJson

View File

@ -4,6 +4,8 @@
#pragma once #pragma once
#include <string.h> // strcmp
namespace ArduinoJson { namespace ArduinoJson {
namespace Internals { namespace Internals {

View File

@ -26,6 +26,7 @@ class StlString {
} }
bool equals(const char* expected) const { bool equals(const char* expected) const {
if (!expected) return false;
return *_str == expected; return *_str == expected;
} }

View File

@ -68,6 +68,7 @@ add_subdirectory(DynamicJsonBuffer)
add_subdirectory(IntegrationTests) add_subdirectory(IntegrationTests)
add_subdirectory(JsonArray) add_subdirectory(JsonArray)
add_subdirectory(JsonDeserializer) add_subdirectory(JsonDeserializer)
add_subdirectory(JsonDocument)
add_subdirectory(JsonObject) add_subdirectory(JsonObject)
add_subdirectory(JsonSerializer) add_subdirectory(JsonSerializer)
add_subdirectory(JsonVariant) add_subdirectory(JsonVariant)

View File

@ -30,8 +30,4 @@ TEST_CASE("JsonArray::begin()/end()") {
SECTION("Mutable") { SECTION("Mutable") {
run_iterator_test<JsonArray::iterator>(); run_iterator_test<JsonArray::iterator>();
} }
SECTION("Const") {
run_iterator_test<JsonArray::const_iterator>();
}
} }

View File

@ -0,0 +1,11 @@
# ArduinoJson - arduinojson.org
# Copyright Benoit Blanchon 2014-2018
# MIT License
add_executable(JsonDocumentTests
DynamicJsonDocument.cpp
StaticJsonDocument.cpp
)
target_link_libraries(JsonDocumentTests catch)
add_test(JsonDocument JsonDocumentTests)

View File

@ -0,0 +1,20 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("DynamicJsonDocument") {
DynamicJsonDocument doc;
SECTION("serializeJson()") {
JsonObject obj = doc.to<JsonObject>();
obj["hello"] = "world";
std::string json;
serializeJson(doc, json);
REQUIRE(json == "{\"hello\":\"world\"}");
}
}

View File

@ -0,0 +1,20 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("StaticJsonDocument") {
StaticJsonDocument<200> doc;
SECTION("serializeJson()") {
JsonObject obj = doc.to<JsonObject>();
obj["hello"] = "world";
std::string json;
serializeJson(doc, json);
REQUIRE(json == "{\"hello\":\"world\"}");
}
}

View File

@ -16,36 +16,28 @@ TEST_CASE("JsonObject::begin()/end()") {
SECTION("NonConstIterator") { SECTION("NonConstIterator") {
JsonObject::iterator it = obj.begin(); JsonObject::iterator it = obj.begin();
REQUIRE(obj.end() != it); REQUIRE(obj.end() != it);
REQUIRE_THAT(it->key, Equals("ab")); REQUIRE_THAT(it->key(), Equals("ab"));
REQUIRE(12 == it->value); REQUIRE(12 == it->value());
it->key = "a.b";
it->value = 1.2;
++it; ++it;
REQUIRE(obj.end() != it); REQUIRE(obj.end() != it);
REQUIRE_THAT(it->key, Equals("cd")); REQUIRE_THAT(it->key(), Equals("cd"));
REQUIRE(34 == it->value); REQUIRE(34 == it->value());
it->key = "c.d";
it->value = 3.4;
++it; ++it;
REQUIRE(obj.end() == it); REQUIRE(obj.end() == it);
REQUIRE(2 == obj.size());
REQUIRE(1.2 == obj["a.b"]);
REQUIRE(3.4 == obj["c.d"]);
} }
SECTION("ConstIterator") { // SECTION("ConstIterator") {
const JsonObject const_object = obj; // const JsonObject const_object = obj;
JsonObject::const_iterator it = const_object.begin(); // JsonObject::iterator it = const_object.begin();
REQUIRE(const_object.end() != it); // REQUIRE(const_object.end() != it);
REQUIRE_THAT(it->key, Equals("ab")); // REQUIRE_THAT(it->key(), Equals("ab"));
REQUIRE(12 == it->value); // REQUIRE(12 == it->value());
++it; // ++it;
REQUIRE(const_object.end() != it); // REQUIRE(const_object.end() != it);
REQUIRE_THAT(it->key, Equals("cd")); // REQUIRE_THAT(it->key(), Equals("cd"));
REQUIRE(34 == it->value); // REQUIRE(34 == it->value());
++it; // ++it;
REQUIRE(const_object.end() == it); // REQUIRE(const_object.end() == it);
} // }
} }

View File

@ -32,7 +32,7 @@ TEST_CASE("JsonObject::remove()") {
obj["c"] = 2; obj["c"] = 2;
for (JsonObject::iterator it = obj.begin(); it != obj.end(); ++it) { for (JsonObject::iterator it = obj.begin(); it != obj.end(); ++it) {
if (it->value == 1) obj.remove(it); if (it->value() == 1) obj.remove(it);
} }
std::string result; std::string result;

View File

@ -6,9 +6,12 @@
#include <catch.hpp> #include <catch.hpp>
#include <limits> #include <limits>
void check(JsonVariant variant, const std::string &expected) { template <typename T>
void check(T value, const std::string &expected) {
DynamicJsonDocument doc;
doc.to<JsonVariant>().set(value);
char buffer[256] = ""; char buffer[256] = "";
size_t returnValue = serializeJson(variant, buffer, sizeof(buffer)); size_t returnValue = serializeJson(doc, buffer, sizeof(buffer));
REQUIRE(expected == buffer); REQUIRE(expected == buffer);
REQUIRE(expected.size() == returnValue); REQUIRE(expected.size() == returnValue);
} }
@ -22,10 +25,22 @@ TEST_CASE("serializeJson(JsonVariant)") {
check(static_cast<char *>(0), "null"); check(static_cast<char *>(0), "null");
} }
SECTION("String") { SECTION("const char*") {
check("hello", "\"hello\""); check("hello", "\"hello\"");
} }
SECTION("string") {
check(std::string("hello"), "\"hello\"");
}
SECTION("SerializedValue<const char*>") {
check(serialized("[1,2]"), "[1,2]");
}
SECTION("SerializedValue<std::string>") {
check(serialized(std::string("[1,2]")), "[1,2]");
}
SECTION("Double") { SECTION("Double") {
check(3.1415927, "3.1415927"); check(3.1415927, "3.1415927");
} }

View File

@ -11,16 +11,18 @@ TEST_CASE("operator<<(std::ostream)") {
std::ostringstream os; std::ostringstream os;
SECTION("JsonVariant containing false") { SECTION("JsonVariant containing false") {
JsonVariant variant = false; JsonVariant variant = doc.to<JsonVariant>();
variant.set(false);
os << variant; os << variant;
REQUIRE("false" == os.str()); REQUIRE("false" == os.str());
} }
SECTION("JsonVariant containing string") { SECTION("JsonVariant containing string") {
JsonVariant variant = "coucou"; JsonVariant variant = doc.to<JsonVariant>();
variant.set("coucou");
os << variant; os << variant;
REQUIRE("\"coucou\"" == os.str()); REQUIRE("\"coucou\"" == os.str());

View File

@ -5,7 +5,6 @@
add_executable(JsonVariantTests add_executable(JsonVariantTests
as.cpp as.cpp
compare.cpp compare.cpp
copy.cpp
is.cpp is.cpp
isnull.cpp isnull.cpp
or.cpp or.cpp

View File

@ -9,222 +9,225 @@
static const char* null = 0; static const char* null = 0;
TEST_CASE("JsonVariant::as()") { TEST_CASE("JsonVariant::as()") {
DynamicJsonDocument doc;
JsonVariant variant = doc.to<JsonVariant>();
SECTION("DoubleAsBool") { SECTION("DoubleAsBool") {
JsonVariant variant = 4.2; variant.set(4.2);
REQUIRE(variant.as<bool>()); REQUIRE(variant.as<bool>());
} }
SECTION("DoubleAsCstr") { SECTION("DoubleAsCstr") {
JsonVariant variant = 4.2; variant.set(4.2);
REQUIRE_FALSE(variant.as<const char*>()); REQUIRE_FALSE(variant.as<const char*>());
} }
SECTION("DoubleAsString") { SECTION("DoubleAsString") {
JsonVariant variant = 4.2; variant.set(4.2);
REQUIRE(std::string("4.2") == variant.as<std::string>()); REQUIRE(std::string("4.2") == variant.as<std::string>());
} }
SECTION("DoubleAsLong") { SECTION("DoubleAsLong") {
JsonVariant variant = 4.2; variant.set(4.2);
REQUIRE(4L == variant.as<long>()); REQUIRE(4L == variant.as<long>());
} }
SECTION("DoubleAsUnsigned") { SECTION("DoubleAsUnsigned") {
JsonVariant variant = 4.2; variant.set(4.2);
REQUIRE(4U == variant.as<unsigned>()); REQUIRE(4U == variant.as<unsigned>());
} }
SECTION("DoubleZeroAsBool") { SECTION("DoubleZeroAsBool") {
JsonVariant variant = 0.0; variant.set(0.0);
REQUIRE_FALSE(variant.as<bool>()); REQUIRE_FALSE(variant.as<bool>());
} }
SECTION("DoubleZeroAsLong") { SECTION("DoubleZeroAsLong") {
JsonVariant variant = 0.0; variant.set(0.0);
REQUIRE(0L == variant.as<long>()); REQUIRE(0L == variant.as<long>());
} }
SECTION("FalseAsBool") { SECTION("FalseAsBool") {
JsonVariant variant = false; variant.set(false);
REQUIRE_FALSE(variant.as<bool>()); REQUIRE_FALSE(variant.as<bool>());
} }
SECTION("FalseAsDouble") { SECTION("FalseAsDouble") {
JsonVariant variant = false; variant.set(false);
REQUIRE(0.0 == variant.as<double>()); REQUIRE(0.0 == variant.as<double>());
} }
SECTION("FalseAsLong") { SECTION("FalseAsLong") {
JsonVariant variant = false; variant.set(false);
REQUIRE(0L == variant.as<long>()); REQUIRE(0L == variant.as<long>());
} }
SECTION("FalseAsString") { SECTION("FalseAsString") {
JsonVariant variant = false; variant.set(false);
REQUIRE(std::string("false") == variant.as<std::string>()); REQUIRE(std::string("false") == variant.as<std::string>());
} }
SECTION("TrueAsBool") { SECTION("TrueAsBool") {
JsonVariant variant = true; variant.set(true);
REQUIRE(variant.as<bool>()); REQUIRE(variant.as<bool>());
} }
SECTION("TrueAsDouble") { SECTION("TrueAsDouble") {
JsonVariant variant = true; variant.set(true);
REQUIRE(1.0 == variant.as<double>()); REQUIRE(1.0 == variant.as<double>());
} }
SECTION("TrueAsLong") { SECTION("TrueAsLong") {
JsonVariant variant = true; variant.set(true);
REQUIRE(1L == variant.as<long>()); REQUIRE(1L == variant.as<long>());
} }
SECTION("TrueAsString") { SECTION("TrueAsString") {
JsonVariant variant = true; variant.set(true);
REQUIRE(std::string("true") == variant.as<std::string>()); REQUIRE(std::string("true") == variant.as<std::string>());
} }
SECTION("LongAsBool") { SECTION("LongAsBool") {
JsonVariant variant = 42L; variant.set(42L);
REQUIRE(variant.as<bool>()); REQUIRE(variant.as<bool>());
} }
SECTION("LongZeroAsBool") { SECTION("LongZeroAsBool") {
JsonVariant variant = 0L; variant.set(0L);
REQUIRE_FALSE(variant.as<bool>()); REQUIRE_FALSE(variant.as<bool>());
} }
SECTION("PositiveLongAsDouble") { SECTION("PositiveLongAsDouble") {
JsonVariant variant = 42L; variant.set(42L);
REQUIRE(42.0 == variant.as<double>()); REQUIRE(42.0 == variant.as<double>());
} }
SECTION("NegativeLongAsDouble") { SECTION("NegativeLongAsDouble") {
JsonVariant variant = -42L; variant.set(-42L);
REQUIRE(-42.0 == variant.as<double>()); REQUIRE(-42.0 == variant.as<double>());
} }
SECTION("LongAsString") { SECTION("LongAsString") {
JsonVariant variant = 42L; variant.set(42L);
REQUIRE(std::string("42") == variant.as<std::string>()); REQUIRE(std::string("42") == variant.as<std::string>());
} }
SECTION("LongZeroAsDouble") { SECTION("LongZeroAsDouble") {
JsonVariant variant = 0L; variant.set(0L);
REQUIRE(0.0 == variant.as<double>()); REQUIRE(0.0 == variant.as<double>());
} }
SECTION("NullAsBool") { SECTION("NullAsBool") {
JsonVariant variant = null; variant.set(null);
REQUIRE_FALSE(variant.as<bool>()); REQUIRE_FALSE(variant.as<bool>());
} }
SECTION("NullAsDouble") { SECTION("NullAsDouble") {
JsonVariant variant = null; variant.set(null);
REQUIRE(0.0 == variant.as<double>()); REQUIRE(0.0 == variant.as<double>());
} }
SECTION("NullAsLong") { SECTION("NullAsLong") {
JsonVariant variant = null; variant.set(null);
REQUIRE(0L == variant.as<long>()); REQUIRE(0L == variant.as<long>());
} }
SECTION("NullAsString") { SECTION("NullAsString") {
JsonVariant variant = null; variant.set(null);
REQUIRE(std::string("null") == variant.as<std::string>()); REQUIRE(std::string("null") == variant.as<std::string>());
} }
SECTION("NumberStringAsBool") { SECTION("NumberStringAsBool") {
JsonVariant variant = "42"; variant.set("42");
REQUIRE(variant.as<bool>()); REQUIRE(variant.as<bool>());
} }
SECTION("NumberStringAsLong") { SECTION("NumberStringAsLong") {
JsonVariant variant = "42"; variant.set("42");
REQUIRE(42L == variant.as<long>()); REQUIRE(42L == variant.as<long>());
} }
#if ARDUINOJSON_USE_LONG_LONG || ARDUINOJSON_USE_INT64 #if ARDUINOJSON_USE_LONG_LONG || ARDUINOJSON_USE_INT64
SECTION("NumberStringAsInt64Negative") { SECTION("NumberStringAsInt64Negative") {
JsonVariant variant = "-9223372036854775808"; variant.set("-9223372036854775808");
REQUIRE(-9223372036854775807 - 1 == variant.as<long long>()); REQUIRE(-9223372036854775807 - 1 == variant.as<long long>());
} }
SECTION("NumberStringAsInt64Positive") { SECTION("NumberStringAsInt64Positive") {
JsonVariant variant = "9223372036854775807"; variant.set("9223372036854775807");
REQUIRE(9223372036854775807 == variant.as<long long>()); REQUIRE(9223372036854775807 == variant.as<long long>());
} }
#endif #endif
SECTION("RandomStringAsBool") { SECTION("RandomStringAsBool") {
JsonVariant variant = "hello"; variant.set("hello");
REQUIRE_FALSE(variant.as<bool>()); REQUIRE_FALSE(variant.as<bool>());
} }
SECTION("RandomStringAsLong") { SECTION("RandomStringAsLong") {
JsonVariant variant = "hello"; variant.set("hello");
REQUIRE(0L == variant.as<long>()); REQUIRE(0L == variant.as<long>());
} }
SECTION("RandomStringAsConstCharPtr") { SECTION("RandomStringAsConstCharPtr") {
JsonVariant variant = "hello"; variant.set("hello");
REQUIRE(std::string("hello") == variant.as<const char*>()); REQUIRE(std::string("hello") == variant.as<const char*>());
} }
SECTION("RandomStringAsCharPtr") { SECTION("RandomStringAsCharPtr") {
JsonVariant variant = "hello"; variant.set("hello");
REQUIRE(std::string("hello") == variant.as<char*>()); REQUIRE(std::string("hello") == variant.as<char*>());
} }
SECTION("RandomStringAsString") { SECTION("RandomStringAsString") {
JsonVariant variant = "hello"; variant.set("hello");
REQUIRE(std::string("hello") == variant.as<std::string>()); REQUIRE(std::string("hello") == variant.as<std::string>());
} }
SECTION("TrueStringAsBool") { SECTION("TrueStringAsBool") {
JsonVariant variant = "true"; variant.set("true");
REQUIRE(variant.as<bool>()); REQUIRE(variant.as<bool>());
} }
SECTION("TrueStringAsLong") { SECTION("TrueStringAsLong") {
JsonVariant variant = "true"; variant.set("true");
REQUIRE(1L == variant.as<long>()); REQUIRE(1L == variant.as<long>());
} }
SECTION("ObjectAsString") { SECTION("ObjectAsString") {
DynamicJsonDocument doc; DynamicJsonDocument doc2;
JsonObject obj = doc.to<JsonObject>(); JsonObject obj = doc2.to<JsonObject>();
obj["key"] = "value"; obj["key"] = "value";
JsonVariant variant = obj; variant.set(obj);
REQUIRE(std::string("{\"key\":\"value\"}") == variant.as<std::string>()); REQUIRE(std::string("{\"key\":\"value\"}") == variant.as<std::string>());
} }
SECTION("ArrayAsString") { SECTION("ArrayAsString") {
DynamicJsonDocument doc; DynamicJsonDocument doc2;
JsonArray arr = doc.to<JsonArray>(); JsonArray arr = doc2.to<JsonArray>();
arr.add(4); arr.add(4);
arr.add(2); arr.add(2);
JsonVariant variant = arr; variant.set(arr);
REQUIRE(std::string("[4,2]") == variant.as<std::string>()); REQUIRE(std::string("[4,2]") == variant.as<std::string>());
} }
SECTION("ArrayAsJsonArray") { SECTION("ArrayAsJsonArray") {
DynamicJsonDocument doc; DynamicJsonDocument doc2;
JsonArray arr = doc.to<JsonArray>(); JsonArray arr = doc2.to<JsonArray>();
JsonVariant variant = arr; variant.set(arr);
REQUIRE(arr == variant.as<JsonArray>()); REQUIRE(arr == variant.as<JsonArray>());
REQUIRE(arr == variant.as<JsonArray>()); // <- shorthand REQUIRE(arr == variant.as<JsonArray>()); // <- shorthand
} }
SECTION("ObjectAsJsonObject") { SECTION("ObjectAsJsonObject") {
DynamicJsonDocument doc; DynamicJsonDocument doc2;
JsonObject obj = doc.to<JsonObject>(); JsonObject obj = doc2.to<JsonObject>();
JsonVariant variant = obj; variant.set(obj);
REQUIRE(obj == variant.as<JsonObject>()); REQUIRE(obj == variant.as<JsonObject>());
REQUIRE(obj == variant.as<JsonObject>()); // <- shorthand REQUIRE(obj == variant.as<JsonObject>()); // <- shorthand
} }

View File

@ -8,46 +8,58 @@
static const char* null = 0; static const char* null = 0;
template <typename T> template <typename T>
void checkEquals(JsonVariant a, T b) { void checkEquals(T a, T b) {
REQUIRE(b == a); DynamicJsonDocument doc;
REQUIRE(a == b); JsonVariant variant = doc.to<JsonVariant>();
REQUIRE(b <= a); variant.set(a);
REQUIRE(a <= b);
REQUIRE(b >= a);
REQUIRE(a >= b);
REQUIRE_FALSE(b != a); REQUIRE(b == variant);
REQUIRE_FALSE(a != b); REQUIRE(variant == b);
REQUIRE_FALSE(b > a); REQUIRE(b <= variant);
REQUIRE_FALSE(a > b); REQUIRE(variant <= b);
REQUIRE_FALSE(b < a); REQUIRE(b >= variant);
REQUIRE_FALSE(a < b); REQUIRE(variant >= b);
REQUIRE_FALSE(b != variant);
REQUIRE_FALSE(variant != b);
REQUIRE_FALSE(b > variant);
REQUIRE_FALSE(variant > b);
REQUIRE_FALSE(b < variant);
REQUIRE_FALSE(variant < b);
} }
template <typename T> template <typename T>
void checkGreater(JsonVariant a, T b) { void checkGreater(T a, T b) {
REQUIRE(a > b); DynamicJsonDocument doc;
REQUIRE(b < a); JsonVariant variant = doc.to<JsonVariant>();
REQUIRE(a != b); variant.set(a);
REQUIRE(b != a);
REQUIRE_FALSE(a < b); REQUIRE(variant > b);
REQUIRE_FALSE(b > a); REQUIRE(b < variant);
REQUIRE_FALSE(a == b); REQUIRE(variant != b);
REQUIRE_FALSE(b == a); REQUIRE(b != variant);
REQUIRE_FALSE(variant < b);
REQUIRE_FALSE(b > variant);
REQUIRE_FALSE(variant == b);
REQUIRE_FALSE(b == variant);
} }
template <typename T> template <typename T>
void checkLower(JsonVariant a, T b) { void checkLower(T a, T b) {
REQUIRE(a < b); DynamicJsonDocument doc;
REQUIRE(b > a); JsonVariant variant = doc.to<JsonVariant>();
REQUIRE(a != b); variant.set(a);
REQUIRE(b != a);
REQUIRE_FALSE(a > b); REQUIRE(variant < b);
REQUIRE_FALSE(b < a); REQUIRE(b > variant);
REQUIRE_FALSE(a == b); REQUIRE(variant != b);
REQUIRE_FALSE(b == a); REQUIRE(b != variant);
REQUIRE_FALSE(variant > b);
REQUIRE_FALSE(b < variant);
REQUIRE_FALSE(variant == b);
REQUIRE_FALSE(b == variant);
} }
template <typename T> template <typename T>
@ -99,7 +111,9 @@ TEST_CASE("JsonVariant comparisons") {
} }
SECTION("null") { SECTION("null") {
JsonVariant variant = null; DynamicJsonDocument doc;
JsonVariant variant = doc.to<JsonVariant>();
variant.set(null);
REQUIRE(variant == variant); REQUIRE(variant == variant);
REQUIRE_FALSE(variant != variant); REQUIRE_FALSE(variant != variant);
@ -139,7 +153,9 @@ TEST_CASE("JsonVariant comparisons") {
} }
SECTION("String") { SECTION("String") {
JsonVariant variant = "hello"; DynamicJsonDocument doc;
JsonVariant variant = doc.to<JsonVariant>();
variant.set("hello");
REQUIRE(variant == variant); REQUIRE(variant == variant);
REQUIRE_FALSE(variant != variant); REQUIRE_FALSE(variant != variant);
@ -163,10 +179,15 @@ TEST_CASE("JsonVariant comparisons") {
REQUIRE_FALSE(null == variant); REQUIRE_FALSE(null == variant);
} }
DynamicJsonDocument doc1, doc2, doc3;
JsonVariant variant1 = doc1.to<JsonVariant>();
JsonVariant variant2 = doc2.to<JsonVariant>();
JsonVariant variant3 = doc3.to<JsonVariant>();
SECTION("IntegerInVariant") { SECTION("IntegerInVariant") {
JsonVariant variant1 = 42; variant1.set(42);
JsonVariant variant2 = 42; variant2.set(42);
JsonVariant variant3 = 666; variant3.set(666);
REQUIRE(variant1 == variant2); REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2); REQUIRE_FALSE(variant1 != variant2);
@ -176,9 +197,9 @@ TEST_CASE("JsonVariant comparisons") {
} }
SECTION("StringInVariant") { SECTION("StringInVariant") {
JsonVariant variant1 = "0hello" + 1; // make sure they have variant1.set("0hello" + 1); // make sure they have
JsonVariant variant2 = "1hello" + 1; // different addresses variant2.set("1hello" + 1); // different addresses
JsonVariant variant3 = "world"; variant3.set("world");
REQUIRE(variant1 == variant2); REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2); REQUIRE_FALSE(variant1 != variant2);
@ -188,9 +209,9 @@ TEST_CASE("JsonVariant comparisons") {
} }
SECTION("DoubleInVariant") { SECTION("DoubleInVariant") {
JsonVariant variant1 = 42.0; variant1.set(42.0);
JsonVariant variant2 = 42.0; variant2.set(42.0);
JsonVariant variant3 = 666.0; variant3.set(666.0);
REQUIRE(variant1 == variant2); REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2); REQUIRE_FALSE(variant1 != variant2);
@ -200,9 +221,9 @@ TEST_CASE("JsonVariant comparisons") {
} }
SECTION("BoolInVariant") { SECTION("BoolInVariant") {
JsonVariant variant1 = true; variant1.set(true);
JsonVariant variant2 = true; variant2.set(true);
JsonVariant variant3 = false; variant3.set(false);
REQUIRE(variant1 == variant2); REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2); REQUIRE_FALSE(variant1 != variant2);
@ -212,14 +233,13 @@ TEST_CASE("JsonVariant comparisons") {
} }
SECTION("ArrayInVariant") { SECTION("ArrayInVariant") {
DynamicJsonDocument doc1; DynamicJsonDocument docArr1, docArr2;
JsonArray array1 = doc1.to<JsonArray>(); JsonArray array1 = docArr1.to<JsonArray>();
DynamicJsonDocument doc2; JsonArray array2 = docArr2.to<JsonArray>();
JsonArray array2 = doc2.to<JsonArray>();
JsonVariant variant1 = array1; variant1.set(array1);
JsonVariant variant2 = array1; variant2.set(array1);
JsonVariant variant3 = array2; variant3.set(array2);
REQUIRE(variant1 == variant2); REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2); REQUIRE_FALSE(variant1 != variant2);
@ -229,14 +249,13 @@ TEST_CASE("JsonVariant comparisons") {
} }
SECTION("ObjectInVariant") { SECTION("ObjectInVariant") {
DynamicJsonDocument doc1; DynamicJsonDocument docObj1, docObj2;
JsonObject obj1 = doc1.to<JsonObject>(); JsonObject obj1 = docObj1.to<JsonObject>();
DynamicJsonDocument doc2; JsonObject obj2 = docObj2.to<JsonObject>();
JsonObject obj2 = doc2.to<JsonObject>();
JsonVariant variant1 = obj1; variant1.set(obj1);
JsonVariant variant2 = obj1; variant2.set(obj1);
JsonVariant variant3 = obj2; variant3.set(obj2);
REQUIRE(variant1 == variant2); REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2); REQUIRE_FALSE(variant1 != variant2);
@ -245,22 +264,22 @@ TEST_CASE("JsonVariant comparisons") {
REQUIRE_FALSE(variant1 == variant3); REQUIRE_FALSE(variant1 == variant3);
} }
SECTION("VariantsOfDifferentTypes") { // SECTION("VariantsOfDifferentTypes") {
DynamicJsonDocument doc1; // DynamicJsonDocument doc1;
JsonObject obj = doc1.to<JsonObject>(); // JsonObject obj = doc1.to<JsonObject>();
DynamicJsonDocument doc2; // DynamicJsonDocument doc2;
JsonArray arr = doc2.to<JsonArray>(); // JsonArray arr = doc2.to<JsonArray>();
JsonVariant variants[] = { // JsonVariant variants[] = {
true, 42, 666.667, "hello", arr, obj, // true, 42, 666.667, "hello", arr, obj,
}; // };
size_t n = sizeof(variants) / sizeof(variants[0]); // size_t n = sizeof(variants) / sizeof(variants[0]);
for (size_t i = 0; i < n; i++) { // for (size_t i = 0; i < n; i++) {
for (size_t j = i + 1; j < n; j++) { // for (size_t j = i + 1; j < n; j++) {
REQUIRE(variants[i] != variants[j]); // REQUIRE(variants[i] != variants[j]);
REQUIRE_FALSE(variants[i] == variants[j]); // REQUIRE_FALSE(variants[i] == variants[j]);
} // }
} // }
} // }
} }

View File

@ -1,65 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonVariant copy") {
JsonVariant _variant1;
JsonVariant _variant2;
SECTION("IntegersAreCopiedByValue") {
_variant1 = 123;
_variant2 = _variant1;
_variant1 = 456;
REQUIRE(123 == _variant2.as<int>());
}
SECTION("DoublesAreCopiedByValue") {
_variant1 = 123.45;
_variant2 = _variant1;
_variant1 = 456.78;
REQUIRE(123.45 == _variant2.as<double>());
}
SECTION("BooleansAreCopiedByValue") {
_variant1 = true;
_variant2 = _variant1;
_variant1 = false;
REQUIRE(_variant2.as<bool>());
}
SECTION("StringsAreCopiedByValue") {
_variant1 = "hello";
_variant2 = _variant1;
_variant1 = "world";
REQUIRE(std::string("hello") == _variant2.as<const char*>());
}
SECTION("ObjectsAreCopiedByReference") {
DynamicJsonDocument doc;
JsonObject object = doc.to<JsonObject>();
_variant1 = object;
object["hello"] = "world";
REQUIRE(1 == _variant1.as<JsonObject>().size());
}
SECTION("ArraysAreCopiedByReference") {
DynamicJsonDocument doc;
JsonArray array = doc.to<JsonArray>();
_variant1 = array;
array.add("world");
REQUIRE(1 == _variant1.as<JsonArray>().size());
}
}

View File

@ -5,7 +5,11 @@
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <catch.hpp> #include <catch.hpp>
void checkIsArray(JsonVariant var) { void checkIsArray(JsonArray value) {
DynamicJsonDocument doc;
JsonVariant var = doc.to<JsonVariant>();
var.set(value);
REQUIRE(var.is<JsonArray>()); REQUIRE(var.is<JsonArray>());
REQUIRE(var.is<JsonArray>()); REQUIRE(var.is<JsonArray>());
REQUIRE(var.is<const JsonArray>()); REQUIRE(var.is<const JsonArray>());
@ -16,48 +20,65 @@ void checkIsArray(JsonVariant var) {
REQUIRE_FALSE(var.is<float>()); REQUIRE_FALSE(var.is<float>());
REQUIRE_FALSE(var.is<int>()); REQUIRE_FALSE(var.is<int>());
REQUIRE_FALSE(var.is<long>()); REQUIRE_FALSE(var.is<long>());
REQUIRE_FALSE(var.is<const char*>()); REQUIRE_FALSE(var.is<const char *>());
REQUIRE_FALSE(var.is<JsonObject>()); REQUIRE_FALSE(var.is<JsonObject>());
} }
void checkIsBool(JsonVariant var) { void checkIsBool(bool value) {
DynamicJsonDocument doc;
JsonVariant var = doc.to<JsonVariant>();
var.set(value);
REQUIRE(var.is<bool>()); REQUIRE(var.is<bool>());
REQUIRE_FALSE(var.is<double>()); REQUIRE_FALSE(var.is<double>());
REQUIRE_FALSE(var.is<float>()); REQUIRE_FALSE(var.is<float>());
REQUIRE_FALSE(var.is<int>()); REQUIRE_FALSE(var.is<int>());
REQUIRE_FALSE(var.is<long>()); REQUIRE_FALSE(var.is<long>());
REQUIRE_FALSE(var.is<const char*>()); REQUIRE_FALSE(var.is<const char *>());
REQUIRE_FALSE(var.is<JsonArray>()); REQUIRE_FALSE(var.is<JsonArray>());
REQUIRE_FALSE(var.is<JsonObject>()); REQUIRE_FALSE(var.is<JsonObject>());
} }
void checkIsFloat(JsonVariant var) { void checkIsFloat(double value) {
DynamicJsonDocument doc;
JsonVariant var = doc.to<JsonVariant>();
var.set(value);
REQUIRE(var.is<double>()); REQUIRE(var.is<double>());
REQUIRE(var.is<float>()); REQUIRE(var.is<float>());
REQUIRE_FALSE(var.is<bool>()); REQUIRE_FALSE(var.is<bool>());
REQUIRE_FALSE(var.is<int>()); REQUIRE_FALSE(var.is<int>());
REQUIRE_FALSE(var.is<long>()); REQUIRE_FALSE(var.is<long>());
REQUIRE_FALSE(var.is<const char*>()); REQUIRE_FALSE(var.is<const char *>());
REQUIRE_FALSE(var.is<JsonArray>()); REQUIRE_FALSE(var.is<JsonArray>());
REQUIRE_FALSE(var.is<JsonObject>()); REQUIRE_FALSE(var.is<JsonObject>());
} }
void checkIsInteger(JsonVariant var) { template <typename T>
void checkIsInteger(T value) {
DynamicJsonDocument doc;
JsonVariant var = doc.to<JsonVariant>();
var.set(value);
REQUIRE(var.is<long>()); REQUIRE(var.is<long>());
REQUIRE(var.is<int>()); REQUIRE(var.is<int>());
REQUIRE(var.is<float>()); REQUIRE(var.is<float>());
REQUIRE(var.is<double>()); REQUIRE(var.is<double>());
REQUIRE_FALSE(var.is<bool>()); REQUIRE_FALSE(var.is<bool>());
REQUIRE_FALSE(var.is<const char*>()); REQUIRE_FALSE(var.is<const char *>());
REQUIRE_FALSE(var.is<JsonArray>()); REQUIRE_FALSE(var.is<JsonArray>());
REQUIRE_FALSE(var.is<JsonObject>()); REQUIRE_FALSE(var.is<JsonObject>());
} }
void checkIsString(JsonVariant var) { void checkIsString(const char *value) {
REQUIRE(var.is<const char*>()); DynamicJsonDocument doc;
JsonVariant var = doc.to<JsonVariant>();
var.set(value);
REQUIRE(var.is<const char *>());
REQUIRE_FALSE(var.is<bool>()); REQUIRE_FALSE(var.is<bool>());
REQUIRE_FALSE(var.is<int>()); REQUIRE_FALSE(var.is<int>());

View File

@ -6,39 +6,42 @@
#include <catch.hpp> #include <catch.hpp>
TEST_CASE("JsonVariant::isNull()") { TEST_CASE("JsonVariant::isNull()") {
SECTION("ReturnsFalse_WhenUndefined") { DynamicJsonDocument doc;
JsonVariant variant; JsonVariant variant = doc.to<JsonVariant>();
SECTION("return true when Undefined") {
REQUIRE(variant.isNull() == true); REQUIRE(variant.isNull() == true);
} }
SECTION("ReturnsTrue_WhenInteger") { SECTION("return false when Integer") {
JsonVariant variant = 0; variant.set(42);
REQUIRE(variant.isNull() == false); REQUIRE(variant.isNull() == false);
} }
SECTION("ReturnsTrue_WhenEmptyArray") { SECTION("return false when EmptyArray") {
DynamicJsonDocument doc; DynamicJsonDocument doc2;
JsonArray array = doc.to<JsonArray>(); JsonArray array = doc2.to<JsonArray>();
JsonVariant variant = array; variant.set(array);
REQUIRE(variant.isNull() == false); REQUIRE(variant.isNull() == false);
} }
SECTION("ReturnsTrue_WhenEmptyObject") { SECTION("return false when EmptyObject") {
DynamicJsonDocument doc; DynamicJsonDocument doc2;
JsonObject obj = doc.to<JsonObject>(); JsonObject obj = doc2.to<JsonObject>();
JsonVariant variant = obj; variant.set(obj);
REQUIRE(variant.isNull() == false); REQUIRE(variant.isNull() == false);
} }
SECTION("ReturnsFalse_WhenInvalidArray") { SECTION("return true when InvalidArray") {
JsonVariant variant = JsonArray(); variant.set(JsonArray());
REQUIRE(variant.isNull() == true); REQUIRE(variant.isNull() == true);
} }
SECTION("ReturnsFalse_WhenInvalidObject") { SECTION("return true when InvalidObject") {
JsonVariant variant = JsonObject(); variant.set(JsonObject());
REQUIRE(variant.isNull() == true); REQUIRE(variant.isNull() == true);
} }
} }

View File

@ -5,78 +5,84 @@
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <catch.hpp> #include <catch.hpp>
static const JsonVariant undefined;
static const JsonVariant null = static_cast<const char*>(0);
TEST_CASE("JsonVariant::operator|()") { TEST_CASE("JsonVariant::operator|()") {
SECTION("undefined | const char*") { DynamicJsonDocument doc;
std::string result = undefined | "default"; JsonVariant variant = doc.to<JsonVariant>();
REQUIRE(result == "default");
SECTION("undefined") {
SECTION("undefined | const char*") {
std::string result = variant | "default";
REQUIRE(result == "default");
}
SECTION("undefined | int") {
int result = variant | 42;
REQUIRE(result == 42);
}
SECTION("undefined | bool") {
bool result = variant | true;
REQUIRE(result == true);
}
} }
SECTION("undefined | int") { SECTION("null") {
int result = undefined | 42; variant.set(static_cast<const char*>(0));
REQUIRE(result == 42);
}
SECTION("undefined | bool") { SECTION("null | const char*") {
bool result = undefined | true; std::string result = variant | "default";
REQUIRE(result == true); REQUIRE(result == "default");
} }
SECTION("null | const char*") { SECTION("null | int") {
std::string result = null | "default"; int result = variant | 42;
REQUIRE(result == "default"); REQUIRE(result == 42);
} }
SECTION("null | int") { SECTION("null | bool") {
int result = null | 42; bool result = variant | true;
REQUIRE(result == 42); REQUIRE(result == true);
} }
SECTION("null | bool") {
bool result = null | true;
REQUIRE(result == true);
} }
SECTION("int | const char*") { SECTION("int | const char*") {
JsonVariant variant = 42; variant.set(42);
std::string result = variant | "default"; std::string result = variant | "default";
REQUIRE(result == "default"); REQUIRE(result == "default");
} }
SECTION("int | int") { SECTION("int | int") {
JsonVariant variant = 0; variant.set(0);
int result = variant | 666; int result = variant | 666;
REQUIRE(result == 0); REQUIRE(result == 0);
} }
SECTION("double | int") { SECTION("double | int") {
JsonVariant variant = 42.0; variant.set(42.0);
int result = variant | 666; int result = variant | 666;
REQUIRE(result == 42); REQUIRE(result == 42);
} }
SECTION("bool | bool") { SECTION("bool | bool") {
JsonVariant variant = false; variant.set(false);
bool result = variant | true; bool result = variant | true;
REQUIRE(result == false); REQUIRE(result == false);
} }
SECTION("int | bool") { SECTION("int | bool") {
JsonVariant variant = 0; variant.set(0);
bool result = variant | true; bool result = variant | true;
REQUIRE(result == true); REQUIRE(result == true);
} }
SECTION("const char* | const char*") { SECTION("const char* | const char*") {
JsonVariant variant = "not default"; variant.set("not default");
std::string result = variant | "default"; std::string result = variant | "default";
REQUIRE(result == "not default"); REQUIRE(result == "not default");
} }
SECTION("const char* | int") { SECTION("const char* | int") {
JsonVariant variant = "not default"; variant.set("not default");
int result = variant | 42; int result = variant | 42;
REQUIRE(result == 42); REQUIRE(result == 42);
} }

View File

@ -9,7 +9,10 @@
template <typename T> template <typename T>
void checkValue(T expected) { void checkValue(T expected) {
JsonVariant variant = expected; DynamicJsonDocument doc;
JsonVariant variant = doc.to<JsonVariant>();
variant.set(expected);
REQUIRE(expected == variant.as<T>()); REQUIRE(expected == variant.as<T>());
} }
@ -21,11 +24,15 @@ void checkReference(T &expected) {
template <typename T> template <typename T>
void checkNumericType() { void checkNumericType() {
DynamicJsonDocument docMin, docMax;
JsonVariant variantMin = docMin.to<JsonVariant>();
JsonVariant variantMax = docMax.to<JsonVariant>();
T min = std::numeric_limits<T>::min(); T min = std::numeric_limits<T>::min();
T max = std::numeric_limits<T>::max(); T max = std::numeric_limits<T>::max();
JsonVariant variantMin(min); variantMin.set(min);
JsonVariant variantMax(max); variantMax.set(max);
REQUIRE(min == variantMin.as<T>()); REQUIRE(min == variantMin.as<T>());
REQUIRE(max == variantMax.as<T>()); REQUIRE(max == variantMax.as<T>());
@ -41,9 +48,12 @@ TEST_CASE("JsonVariant set()/get()") {
SECTION("Null") { SECTION("Null") {
checkValue<const char *>(NULL); checkValue<const char *>(NULL);
} }
SECTION("String") { SECTION("const char*") {
checkValue<const char *>("hello"); checkValue<const char *>("hello");
} }
SECTION("std::string") {
checkValue<std::string>("hello");
}
SECTION("False") { SECTION("False") {
checkValue<bool>(false); checkValue<bool>(false);

View File

@ -6,24 +6,26 @@
#include <catch.hpp> #include <catch.hpp>
TEST_CASE("JsonVariant::operator[]") { TEST_CASE("JsonVariant::operator[]") {
DynamicJsonDocument doc;
JsonVariant var = doc.to<JsonVariant>();
SECTION("The JsonVariant is undefined") { SECTION("The JsonVariant is undefined") {
JsonVariant var = JsonVariant();
REQUIRE(0 == var.size()); REQUIRE(0 == var.size());
REQUIRE(var["0"].isNull()); REQUIRE(var["0"].isNull());
REQUIRE(var[0].isNull()); REQUIRE(var[0].isNull());
} }
SECTION("The JsonVariant is a string") { SECTION("The JsonVariant is a string") {
JsonVariant var = "hello world"; var.set("hello world");
REQUIRE(0 == var.size()); REQUIRE(0 == var.size());
REQUIRE(var["0"].isNull()); REQUIRE(var["0"].isNull());
REQUIRE(var[0].isNull()); REQUIRE(var[0].isNull());
} }
SECTION("The JsonVariant is a JsonArray") { SECTION("The JsonVariant is a JsonArray") {
DynamicJsonDocument doc; DynamicJsonDocument doc2;
JsonArray array = doc.to<JsonArray>(); JsonArray array = doc2.to<JsonArray>();
JsonVariant var = array; var.set(array);
SECTION("get value") { SECTION("get value") {
array.add("element at index 0"); array.add("element at index 0");
@ -60,9 +62,9 @@ TEST_CASE("JsonVariant::operator[]") {
} }
SECTION("The JsonVariant is a JsonObject") { SECTION("The JsonVariant is a JsonObject") {
DynamicJsonDocument doc; DynamicJsonDocument doc2;
JsonObject object = doc.to<JsonObject>(); JsonObject object = doc2.to<JsonObject>();
JsonVariant var = object; var.set(object);
SECTION("get value") { SECTION("get value") {
object["a"] = "element at key \"a\""; object["a"] = "element at key \"a\"";

View File

@ -29,19 +29,13 @@ TEST_CASE("unsigned char[]") {
} }
SECTION("JsonVariant") { SECTION("JsonVariant") {
SECTION("constructor") { DynamicJsonDocument doc;
SECTION("set") {
unsigned char value[] = "42"; unsigned char value[] = "42";
JsonVariant variant(value); JsonVariant variant = doc.to<JsonVariant>();
variant.set(value);
REQUIRE(42 == variant.as<int>());
}
SECTION("operator=") {
unsigned char value[] = "42";
JsonVariant variant(666);
variant = value;
REQUIRE(42 == variant.as<int>()); REQUIRE(42 == variant.as<int>());
} }
@ -50,7 +44,6 @@ TEST_CASE("unsigned char[]") {
SECTION("operator[]") { SECTION("operator[]") {
unsigned char key[] = "hello"; unsigned char key[] = "hello";
DynamicJsonDocument doc;
deserializeJson(doc, "{\"hello\":\"world\"}"); deserializeJson(doc, "{\"hello\":\"world\"}");
JsonVariant variant = doc.as<JsonVariant>(); JsonVariant variant = doc.as<JsonVariant>();
@ -62,7 +55,6 @@ TEST_CASE("unsigned char[]") {
SECTION("operator[] const") { SECTION("operator[] const") {
unsigned char key[] = "hello"; unsigned char key[] = "hello";
DynamicJsonDocument doc;
deserializeJson(doc, "{\"hello\":\"world\"}"); deserializeJson(doc, "{\"hello\":\"world\"}");
const JsonVariant variant = doc.as<JsonVariant>(); const JsonVariant variant = doc.as<JsonVariant>();
@ -73,8 +65,8 @@ TEST_CASE("unsigned char[]") {
SECTION("operator==") { SECTION("operator==") {
unsigned char comparand[] = "hello"; unsigned char comparand[] = "hello";
JsonVariant variant; JsonVariant variant = doc.to<JsonVariant>();
variant = "hello"; variant.set("hello");
REQUIRE(comparand == variant); REQUIRE(comparand == variant);
REQUIRE(variant == comparand); REQUIRE(variant == comparand);
@ -85,8 +77,8 @@ TEST_CASE("unsigned char[]") {
SECTION("operator!=") { SECTION("operator!=") {
unsigned char comparand[] = "hello"; unsigned char comparand[] = "hello";
JsonVariant variant; JsonVariant variant = doc.to<JsonVariant>();
variant = "world"; variant.set("world");
REQUIRE(comparand != variant); REQUIRE(comparand != variant);
REQUIRE(variant != comparand); REQUIRE(variant != comparand);

View File

@ -40,23 +40,15 @@ TEST_CASE("Variable Length Array") {
} }
SECTION("JsonVariant") { SECTION("JsonVariant") {
SECTION("constructor") { DynamicJsonDocument doc;
SECTION("set()") {
int i = 16; int i = 16;
char vla[i]; char vla[i];
strcpy(vla, "42"); strcpy(vla, "42");
JsonVariant variant(vla); JsonVariant variant = doc.to<JsonVariant>();
variant.set(vla);
REQUIRE(42 == variant.as<int>());
}
SECTION("operator=") {
int i = 16;
char vla[i];
strcpy(vla, "42");
JsonVariant variant(666);
variant = vla;
REQUIRE(42 == variant.as<int>()); REQUIRE(42 == variant.as<int>());
} }
@ -67,7 +59,6 @@ TEST_CASE("Variable Length Array") {
char vla[i]; char vla[i];
strcpy(vla, "hello"); strcpy(vla, "hello");
DynamicJsonDocument doc;
deserializeJson(doc, "{\"hello\":\"world\"}"); deserializeJson(doc, "{\"hello\":\"world\"}");
JsonVariant variant = doc.as<JsonVariant>(); JsonVariant variant = doc.as<JsonVariant>();
@ -81,7 +72,6 @@ TEST_CASE("Variable Length Array") {
char vla[i]; char vla[i];
strcpy(vla, "hello"); strcpy(vla, "hello");
DynamicJsonDocument doc;
deserializeJson(doc, "{\"hello\":\"world\"}"); deserializeJson(doc, "{\"hello\":\"world\"}");
const JsonVariant variant = doc.as<JsonVariant>(); const JsonVariant variant = doc.as<JsonVariant>();
@ -94,8 +84,8 @@ TEST_CASE("Variable Length Array") {
char vla[i]; char vla[i];
strcpy(vla, "hello"); strcpy(vla, "hello");
JsonVariant variant; JsonVariant variant = doc.to<JsonVariant>();
variant = "hello"; variant.set("hello");
REQUIRE((vla == variant)); REQUIRE((vla == variant));
REQUIRE((variant == vla)); REQUIRE((variant == vla));
@ -109,7 +99,7 @@ TEST_CASE("Variable Length Array") {
strcpy(vla, "hello"); strcpy(vla, "hello");
JsonVariant variant; JsonVariant variant;
variant = "world"; variant.set("world");
REQUIRE((vla != variant)); REQUIRE((vla != variant));
REQUIRE((variant != vla)); REQUIRE((variant != vla));

View File

@ -16,10 +16,18 @@ static void check(const char* input, U expected) {
REQUIRE(variant.as<T>() == expected); REQUIRE(variant.as<T>() == expected);
} }
static void checkIsNull(const char* input) {
DynamicJsonDocument variant;
DeserializationError error = deserializeMsgPack(variant, input);
REQUIRE(error == DeserializationError::Ok);
REQUIRE(variant.as<JsonVariant>().isNull());
}
TEST_CASE("deserialize MsgPack value") { TEST_CASE("deserialize MsgPack value") {
SECTION("nil") { SECTION("nil") {
const char* nil = 0; // ArduinoJson uses a string for null checkIsNull("\xc0");
check<const char*>("\xc0", nil);
} }
SECTION("bool") { SECTION("bool") {

View File

@ -5,8 +5,11 @@
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <catch.hpp> #include <catch.hpp>
void check(JsonVariant variant, const char* expected_data, template <typename T>
size_t expected_len) { void check(T value, const char* expected_data, size_t expected_len) {
DynamicJsonDocument doc;
JsonVariant variant = doc.to<JsonVariant>();
variant.set(value);
std::string expected(expected_data, expected_data + expected_len); std::string expected(expected_data, expected_data + expected_len);
std::string actual; std::string actual;
size_t len = serializeMsgPack(variant, actual); size_t len = serializeMsgPack(variant, actual);
@ -15,14 +18,15 @@ void check(JsonVariant variant, const char* expected_data,
REQUIRE(actual == expected); REQUIRE(actual == expected);
} }
template <size_t N> template <typename T, size_t N>
void check(JsonVariant variant, const char (&expected_data)[N]) { void check(T value, const char (&expected_data)[N]) {
const size_t expected_len = N - 1; const size_t expected_len = N - 1;
check(variant, expected_data, expected_len); check(value, expected_data, expected_len);
} }
void check(JsonVariant variant, const std::string& expected) { template <typename T>
check(variant, expected.data(), expected.length()); void check(T value, const std::string& expected) {
check(value, expected.data(), expected.length());
} }
TEST_CASE("serialize MsgPack value") { TEST_CASE("serialize MsgPack value") {