Added DeserializationOption::Filter (closes #959)

This commit is contained in:
Benoit Blanchon
2020-02-11 21:56:50 +01:00
parent 42b0d6a83d
commit 66b12da4e7
16 changed files with 1095 additions and 69 deletions

View File

@ -1,6 +1,12 @@
ArduinoJson: change log ArduinoJson: change log
======================= =======================
HEAD
----
* Added `DeserializationOption::Filter` (issue #959)
* Added example `JsonFilterExample.ino`
v6.14.1 (2020-01-27) v6.14.1 (2020-01-27)
------- -------

View File

@ -17,6 +17,7 @@ ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things).
* [Optionally decodes UTF-16 escape sequences to UTF-8](https://arduinojson.org/v6/api/config/decode_unicode/) * [Optionally decodes UTF-16 escape sequences to UTF-8](https://arduinojson.org/v6/api/config/decode_unicode/)
* [Optionally stores links to the input buffer (zero-copy)](https://arduinojson.org/v6/api/json/deserializejson/) * [Optionally stores links to the input buffer (zero-copy)](https://arduinojson.org/v6/api/json/deserializejson/)
* [Optionally supports comments in the input](https://arduinojson.org/v6/api/config/enable_comments/) * [Optionally supports comments in the input](https://arduinojson.org/v6/api/config/enable_comments/)
* Optionally filters the input to keep only desired values
* Supports single quotes as a string delimiter * Supports single quotes as a string delimiter
* Compatible with NDJSON and JSON Lines * Compatible with NDJSON and JSON Lines
* [JSON serialization](https://arduinojson.org/v6/api/json/serializejson/) * [JSON serialization](https://arduinojson.org/v6/api/json/serializejson/)

View File

@ -0,0 +1,66 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
//
// This example shows how to use DeserializationOpion::Filter
//
// https://arduinojson.org/v6/example/filter/
#include <ArduinoJson.h>
void setup() {
// Initialize serial port
Serial.begin(9600);
while (!Serial) continue;
// The huge input: an extract from OpenWeatherMap response
const __FlashStringHelper* input_json = F(
"{\"cod\":\"200\",\"message\":0,\"list\":[{\"dt\":1581498000,\"main\":{"
"\"temp\":3.23,\"feels_like\":-3.63,\"temp_min\":3.23,\"temp_max\":4.62,"
"\"pressure\":1014,\"sea_level\":1014,\"grnd_level\":1010,\"humidity\":"
"58,\"temp_kf\":-1.39},\"weather\":[{\"id\":800,\"main\":\"Clear\","
"\"description\":\"clear "
"sky\",\"icon\":\"01d\"}],\"clouds\":{\"all\":0},\"wind\":{\"speed\":6."
"19,\"deg\":266},\"sys\":{\"pod\":\"d\"},\"dt_txt\":\"2020-02-12 "
"09:00:00\"},{\"dt\":1581508800,\"main\":{\"temp\":6.09,\"feels_like\":-"
"1.07,\"temp_min\":6.09,\"temp_max\":7.13,\"pressure\":1015,\"sea_"
"level\":1015,\"grnd_level\":1011,\"humidity\":48,\"temp_kf\":-1.04},"
"\"weather\":[{\"id\":800,\"main\":\"Clear\",\"description\":\"clear "
"sky\",\"icon\":\"01d\"}],\"clouds\":{\"all\":9},\"wind\":{\"speed\":6."
"64,\"deg\":268},\"sys\":{\"pod\":\"d\"},\"dt_txt\":\"2020-02-12 "
"12:00:00\"}],\"city\":{\"id\":2643743,\"name\":\"London\",\"coord\":{"
"\"lat\":51.5085,\"lon\":-0.1257},\"country\":\"GB\",\"population\":"
"1000000,\"timezone\":0,\"sunrise\":1581492085,\"sunset\":1581527294}}");
// The filter: it contains "true" for each value we want to keep
const __FlashStringHelper* filter_json =
F("{\"list\":[{\"dt\":true,\"main\":{\"temp\":true}]}");
// Create the filter document
StaticJsonDocument<200> filter;
deserializeJson(filter, filter_json);
// Deserialize the document
StaticJsonDocument<400> doc;
deserializeJson(doc, input_json, DeserializationOption::Filter(filter));
// Print the result
serializeJsonPretty(doc, Serial);
}
void loop() {
// not used in this example
}
// See also
// --------
//
// https://arduinojson.org/ contains the documentation for all the functions
// used above. It also includes an FAQ that will help you solve any
// deserialization problem.
//
// The book "Mastering ArduinoJson" contains a tutorial on deserialization.
// It begins with a simple example, like the one above, and then adds more
// features like deserializing directly from a file or an HTTP request.
// Learn more at https://arduinojson.org/book/
// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤

View File

@ -6,6 +6,7 @@ add_executable(IntegrationTests
gbathree.cpp gbathree.cpp
issue772.cpp issue772.cpp
round_trip.cpp round_trip.cpp
openweathermap.cpp
) )
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")

File diff suppressed because one or more lines are too long

View File

@ -6,12 +6,13 @@ add_executable(JsonDeserializerTests
array.cpp array.cpp
array_static.cpp array_static.cpp
DeserializationError.cpp DeserializationError.cpp
filter.cpp
incomplete_input.cpp incomplete_input.cpp
input_types.cpp input_types.cpp
number.cpp
invalid_input.cpp invalid_input.cpp
misc.cpp misc.cpp
nestingLimit.cpp nestingLimit.cpp
number.cpp
object.cpp object.cpp
object_static.cpp object_static.cpp
string.cpp string.cpp

View File

@ -0,0 +1,577 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include <sstream>
#include <string>
TEST_CASE("Filtering") {
struct TestCase {
const char* input;
const char* filter;
uint8_t nestingLimit;
DeserializationError error;
const char* output;
size_t memoryUsage;
};
// clang-format off
TestCase testCases[] = {
{
"{\"hello\":\"world\"}", // 1. input
"null", // 2. filter
10, // 3. nestingLimit
DeserializationError::Ok, // 4. error
"null", // 5. output
0 // 6. memoryUsage
},
{
"{\"hello\":\"world\"}",
"false",
10,
DeserializationError::Ok,
"null",
0
},
{
"{\"abcdefg\":\"hijklmn\"}",
"true",
10,
DeserializationError::Ok,
"{\"abcdefg\":\"hijklmn\"}",
JSON_OBJECT_SIZE(1) + 16
},
{
"{\"hello\":\"world\"}",
"{}",
10,
DeserializationError::Ok,
"{}",
JSON_OBJECT_SIZE(0)
},
{
// Input in an object, but filter wants an array
"{\"hello\":\"world\"}",
"[]",
10,
DeserializationError::Ok,
"null",
0
},
{
// Input is an array, but filter wants an object
"[\"hello\",\"world\"]",
"{}",
10,
DeserializationError::Ok,
"null",
0
},
{
// Input is a bool, but filter wants an object
"true",
"{}",
10,
DeserializationError::Ok,
"null",
0
},
{
// Input is a string, but filter wants an object
"\"hello\"",
"{}",
10,
DeserializationError::Ok,
"null",
0
},
{
// skip an integer
"{\"an_integer\":666,example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
JSON_OBJECT_SIZE(1) + 8
},
{
// skip a float
"{\"a_float\":12.34e-6,example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
JSON_OBJECT_SIZE(1) + 8
},
{
// can skip a boolean
"{\"a_bool\":false,example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
JSON_OBJECT_SIZE(1) + 8
},
{
// can skip a double-quoted string
"{\"a_double_quoted_string\":\"hello\",example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
JSON_OBJECT_SIZE(1) + 8
},
{
// can skip a single-quoted string
"{\"a_single_quoted_string\":'hello',example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
JSON_OBJECT_SIZE(1) + 8
},
{
// can skip an empty array
"{\"an_empty_array\":[],example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
JSON_OBJECT_SIZE(1) + 8
},
{
// can skip an empty array with spaces in it
"{\"an_empty_array\":[\t],example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
JSON_OBJECT_SIZE(1) + 8
},
{
// can skip an array
"{\"an_array\":[1,2,3],example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
JSON_OBJECT_SIZE(1) + 8
},
{
// can skip an array with spaces in it
"{\"an_array\": [ 1 , 2 , 3 ] ,example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
JSON_OBJECT_SIZE(1) + 8
},
{
// can skip an empty object
"{\"an_empty_object\":{},example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
JSON_OBJECT_SIZE(1) + 8
},
{
// can skip an empty object with spaces in it
"{\"an_empty_object\":{ },example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
JSON_OBJECT_SIZE(1) + 8
},
{
// can skip an object
"{\"an_object\":{a:1,'b':2,\"c\":3},example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
JSON_OBJECT_SIZE(1) + 8
},
{
// skip an object with spaces in it
"{\"an_object\" : { a : 1 , 'b' : 2 , \"c\" : 3 } ,example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
JSON_OBJECT_SIZE(1) + 8
},
{
"{\"an_integer\": 0,\"example\":{\"type\":\"int\",\"outcome\":42}}",
"{\"example\":{\"outcome\":true}}",
10,
DeserializationError::Ok,
"{\"example\":{\"outcome\":42}}",
2 * JSON_OBJECT_SIZE(1) + 16
},
{
// only the first element of array counts
"[1,2,3]",
"[true, false]",
10,
DeserializationError::Ok,
"[1,2,3]",
JSON_ARRAY_SIZE(3)
},
{
// only the first element of array counts
"[1,2,3]",
"[false, true]",
10,
DeserializationError::Ok,
"[]",
JSON_ARRAY_SIZE(0)
},
{
// filter members of object in array
"[{\"example\":1,\"ignore\":2},{\"example\":3,\"ignore\":4}]",
"[{\"example\":true}]",
10,
DeserializationError::Ok,
"[{\"example\":1},{\"example\":3}]",
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 16
},
{
"[',2,3]",
"[false,true]",
10,
DeserializationError::IncompleteInput,
"[]",
JSON_ARRAY_SIZE(0)
},
{
"[\",2,3]",
"[false,true]",
10,
DeserializationError::IncompleteInput,
"[]",
JSON_ARRAY_SIZE(0)
},
{
// ignore errors in skipped value
"[!,2,\\]",
"[false]",
10,
DeserializationError::Ok,
"[]",
JSON_ARRAY_SIZE(0)
},
{
// detect incomplete string event if it's skipped
"\"ABC",
"false",
10,
DeserializationError::IncompleteInput,
"null",
0
},
{
// detect incomplete string event if it's skipped
"'ABC",
"false",
10,
DeserializationError::IncompleteInput,
"null",
0
},
{
// handle escaped quotes
"'A\\'BC'",
"false",
10,
DeserializationError::Ok,
"null",
0
},
{
// handle escaped quotes
"\"A\\\"BC\"",
"false",
10,
DeserializationError::Ok,
"null",
0
},
{
// detect incomplete string in presence of escaped quotes
"'A\\'BC",
"false",
10,
DeserializationError::IncompleteInput,
"null",
0
},
{
// detect incomplete string in presence of escaped quotes
"\"A\\\"BC",
"false",
10,
DeserializationError::IncompleteInput,
"null",
0
},
{
// skip empty array
"[]",
"false",
10,
DeserializationError::Ok,
"null",
0
},
{
// skip empty array with spaces
" [ ] ",
"false",
10,
DeserializationError::Ok,
"null",
0
},
{
// bubble up element error even if array is skipped
"[1,'2,3]",
"false",
10,
DeserializationError::IncompleteInput,
"null",
0
},
{
// bubble up member error even if object is skipped
"{'hello':'worl}",
"false",
10,
DeserializationError::IncompleteInput,
"null",
0
},
{
// bubble up colon error even if object is skipped
"{'hello','world'}",
"false",
10,
DeserializationError::InvalidInput,
"null",
0
},
{
// bubble up key error even if object is skipped
"{'hello:1}",
"false",
10,
DeserializationError::IncompleteInput,
"null",
0
},
{
// ignore invalid value in skipped object
"{'hello':!}",
"false",
10,
DeserializationError::Ok,
"null",
0
},
{
// ignore invalid value in skipped object
"{'hello':\\}",
"false",
10,
DeserializationError::Ok,
"null",
0
},
{
// check nesting limit even for ignored objects
"{}",
"false",
0,
DeserializationError::TooDeep,
"null",
0
},
{
// check nesting limit even for ignored objects
"{'hello':{}}",
"false",
1,
DeserializationError::TooDeep,
"null",
0
},
{
// check nesting limit even for ignored values in objects
"{'hello':{}}",
"{}",
1,
DeserializationError::TooDeep,
"{}",
JSON_OBJECT_SIZE(0)
},
{
// check nesting limit even for ignored arrays
"[]",
"false",
0,
DeserializationError::TooDeep,
"null",
0
},
{
// check nesting limit even for ignored arrays
"[[]]",
"false",
1,
DeserializationError::TooDeep,
"null",
0
},
{
// check nesting limit even for ignored values in arrays
"[[]]",
"[]",
1,
DeserializationError::TooDeep,
"[]",
JSON_ARRAY_SIZE(0)
},
}; // clang-format on
for (size_t i = 0; i < sizeof(testCases) / sizeof(testCases[0]); i++) {
CAPTURE(i);
DynamicJsonDocument filter(256);
DynamicJsonDocument doc(256);
TestCase& tc = testCases[i];
CAPTURE(tc.filter);
REQUIRE(deserializeJson(filter, tc.filter) == DeserializationError::Ok);
CAPTURE(tc.input);
CAPTURE(tc.nestingLimit);
CHECK(deserializeJson(doc, tc.input, DeserializationOption::Filter(filter),
DeserializationOption::NestingLimit(
tc.nestingLimit)) == tc.error);
CHECK(doc.as<std::string>() == tc.output);
CHECK(doc.memoryUsage() == tc.memoryUsage);
}
}
TEST_CASE("Overloads") {
StaticJsonDocument<256> doc;
StaticJsonDocument<256> filter;
using namespace DeserializationOption;
// deserializeJson(..., Filter)
SECTION("const char*, Filter") {
deserializeJson(doc, "{}", Filter(filter));
}
SECTION("const char*, size_t, Filter") {
deserializeJson(doc, "{}", 2, Filter(filter));
}
SECTION("const std::string&, Filter") {
deserializeJson(doc, std::string("{}"), Filter(filter));
}
SECTION("std::istream&, Filter") {
std::stringstream s("{}");
deserializeJson(doc, s, Filter(filter));
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("char[n], Filter") {
int i = 4;
char vla[i];
strcpy(vla, "{}");
deserializeJson(doc, vla, Filter(filter));
}
#endif
// deserializeJson(..., Filter, NestingLimit)
SECTION("const char*, Filter, NestingLimit") {
deserializeJson(doc, "{}", Filter(filter), NestingLimit(5));
}
SECTION("const char*, size_t, Filter, NestingLimit") {
deserializeJson(doc, "{}", 2, Filter(filter), NestingLimit(5));
}
SECTION("const std::string&, Filter, NestingLimit") {
deserializeJson(doc, std::string("{}"), Filter(filter), NestingLimit(5));
}
SECTION("std::istream&, Filter, NestingLimit") {
std::stringstream s("{}");
deserializeJson(doc, s, Filter(filter), NestingLimit(5));
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("char[n], Filter, NestingLimit") {
int i = 4;
char vla[i];
strcpy(vla, "{}");
deserializeJson(doc, vla, Filter(filter), NestingLimit(5));
}
#endif
// deserializeJson(..., NestingLimit, Filter)
SECTION("const char*, NestingLimit, Filter") {
deserializeJson(doc, "{}", NestingLimit(5), Filter(filter));
}
SECTION("const char*, size_t, NestingLimit, Filter") {
deserializeJson(doc, "{}", 2, NestingLimit(5), Filter(filter));
}
SECTION("const std::string&, NestingLimit, Filter") {
deserializeJson(doc, std::string("{}"), NestingLimit(5), Filter(filter));
}
SECTION("std::istream&, NestingLimit, Filter") {
std::stringstream s("{}");
deserializeJson(doc, s, NestingLimit(5), Filter(filter));
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("char[n], NestingLimit, Filter") {
int i = 4;
char vla[i];
strcpy(vla, "{}");
deserializeJson(doc, vla, NestingLimit(5), Filter(filter));
}
#endif
}
TEST_CASE("StringMover::reclaim()") {
StaticJsonDocument<200> filter;
filter["a"] = true;
filter["c"] = true;
char input[] = "{\"a\":1,\"b\":2,\"c\":1}";
StaticJsonDocument<200> doc;
deserializeJson(doc, input, DeserializationOption::Filter(filter));
REQUIRE(doc.as<std::string>() == "{\"a\":1,\"c\":1}");
CHECK(input[0] == 'a');
CHECK(input[1] == 0);
CHECK(input[2] == 'c');
CHECK(input[3] == 0);
}

View File

@ -65,6 +65,7 @@ using ARDUINOJSON_NAMESPACE::serializeMsgPack;
using ARDUINOJSON_NAMESPACE::StaticJsonDocument; using ARDUINOJSON_NAMESPACE::StaticJsonDocument;
namespace DeserializationOption { namespace DeserializationOption {
using ARDUINOJSON_NAMESPACE::Filter;
using ARDUINOJSON_NAMESPACE::NestingLimit; using ARDUINOJSON_NAMESPACE::NestingLimit;
} } // namespace DeserializationOption
} // namespace ArduinoJson } // namespace ArduinoJson

View File

@ -0,0 +1,66 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#pragma once
#include <ArduinoJson/Namespace.hpp>
namespace ARDUINOJSON_NAMESPACE {
class Filter {
public:
explicit Filter(VariantConstRef v) : _variant(v) {}
bool allow() const {
return _variant;
}
bool allowArray() const {
return _variant == true || _variant.is<ArrayRef>();
}
bool allowObject() const {
return _variant == true || _variant.is<ObjectRef>();
}
bool allowValue() const {
return _variant == true;
}
template <typename TKey>
Filter operator[](const TKey& key) const {
if (_variant == true) // "true" means "allow recursively"
return *this;
else
return Filter(_variant[key]);
}
private:
VariantConstRef _variant;
};
struct AllowAllFilter {
bool allow() const {
return true;
}
bool allowArray() const {
return true;
}
bool allowObject() const {
return true;
}
bool allowValue() const {
return true;
}
template <typename TKey>
AllowAllFilter operator[](const TKey&) const {
return AllowAllFilter();
}
};
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -5,6 +5,7 @@
#pragma once #pragma once
#include <ArduinoJson/Deserialization/DeserializationError.hpp> #include <ArduinoJson/Deserialization/DeserializationError.hpp>
#include <ArduinoJson/Deserialization/Filter.hpp>
#include <ArduinoJson/Deserialization/NestingLimit.hpp> #include <ArduinoJson/Deserialization/NestingLimit.hpp>
#include <ArduinoJson/Deserialization/Reader.hpp> #include <ArduinoJson/Deserialization/Reader.hpp>
#include <ArduinoJson/StringStorage/StringStorage.hpp> #include <ArduinoJson/StringStorage/StringStorage.hpp>
@ -19,47 +20,52 @@ TDeserializer<TReader, TWriter> makeDeserializer(MemoryPool &pool,
return TDeserializer<TReader, TWriter>(pool, reader, writer, nestingLimit); return TDeserializer<TReader, TWriter>(pool, reader, writer, nestingLimit);
} }
// deserialize(JsonDocument&, const std::string&); // deserialize(JsonDocument&, const std::string&, NestingLimit, Filter);
// deserialize(JsonDocument&, const String&); // deserialize(JsonDocument&, const String&, NestingLimit, Filter);
// deserialize(JsonDocument&, char*); // deserialize(JsonDocument&, char*, NestingLimit, Filter);
// deserialize(JsonDocument&, const char*); // deserialize(JsonDocument&, const char*, NestingLimit, Filter);
// deserialize(JsonDocument&, const __FlashStringHelper*); // deserialize(JsonDocument&, const __FlashStringHelper*, NestingLimit, Filter);
template <template <typename, typename> class TDeserializer, typename TString> template <template <typename, typename> class TDeserializer, typename TString,
typename TFilter>
typename enable_if<!is_array<TString>::value, DeserializationError>::type typename enable_if<!is_array<TString>::value, DeserializationError>::type
deserialize(JsonDocument &doc, const TString &input, deserialize(JsonDocument &doc, const TString &input, NestingLimit nestingLimit,
NestingLimit nestingLimit) { TFilter filter) {
Reader<TString> reader(input); Reader<TString> reader(input);
doc.clear(); doc.clear();
return makeDeserializer<TDeserializer>( return makeDeserializer<TDeserializer>(
doc.memoryPool(), reader, doc.memoryPool(), reader,
makeStringStorage(doc.memoryPool(), input), nestingLimit.value) makeStringStorage(doc.memoryPool(), input), nestingLimit.value)
.parse(doc.data()); .parse(doc.data(), filter);
} }
// //
// deserialize(JsonDocument&, char*, size_t); // deserialize(JsonDocument&, char*, size_t, NestingLimit, Filter);
// deserialize(JsonDocument&, const char*, size_t); // deserialize(JsonDocument&, const char*, size_t, NestingLimit, Filter);
// deserialize(JsonDocument&, const __FlashStringHelper*, size_t); // deserialize(JsonDocument&, const __FlashStringHelper*, size_t, NL, Filter);
template <template <typename, typename> class TDeserializer, typename TChar> template <template <typename, typename> class TDeserializer, typename TChar,
typename TFilter>
DeserializationError deserialize(JsonDocument &doc, TChar *input, DeserializationError deserialize(JsonDocument &doc, TChar *input,
size_t inputSize, NestingLimit nestingLimit) { size_t inputSize, NestingLimit nestingLimit,
TFilter filter) {
BoundedReader<TChar *> reader(input, inputSize); BoundedReader<TChar *> reader(input, inputSize);
doc.clear(); doc.clear();
return makeDeserializer<TDeserializer>( return makeDeserializer<TDeserializer>(
doc.memoryPool(), reader, doc.memoryPool(), reader,
makeStringStorage(doc.memoryPool(), input), nestingLimit.value) makeStringStorage(doc.memoryPool(), input), nestingLimit.value)
.parse(doc.data()); .parse(doc.data(), filter);
} }
// //
// deserialize(JsonDocument&, std::istream&); // deserialize(JsonDocument&, std::istream&, NestingLimit, Filter);
// deserialize(JsonDocument&, Stream&); // deserialize(JsonDocument&, Stream&, NestingLimit, Filter);
template <template <typename, typename> class TDeserializer, typename TStream> template <template <typename, typename> class TDeserializer, typename TStream,
typename TFilter>
DeserializationError deserialize(JsonDocument &doc, TStream &input, DeserializationError deserialize(JsonDocument &doc, TStream &input,
NestingLimit nestingLimit) { NestingLimit nestingLimit, TFilter filter) {
Reader<TStream> reader(input); Reader<TStream> reader(input);
doc.clear(); doc.clear();
return makeDeserializer<TDeserializer>( return makeDeserializer<TDeserializer>(
doc.memoryPool(), reader, doc.memoryPool(), reader,
makeStringStorage(doc.memoryPool(), input), nestingLimit.value) makeStringStorage(doc.memoryPool(), input), nestingLimit.value)
.parse(doc.data()); .parse(doc.data(), filter);
} }
} // namespace ARDUINOJSON_NAMESPACE } // namespace ARDUINOJSON_NAMESPACE

View File

@ -27,9 +27,15 @@ class JsonDeserializer {
_reader(reader), _reader(reader),
_stringStorage(stringStorage), _stringStorage(stringStorage),
_nestingLimit(nestingLimit), _nestingLimit(nestingLimit),
_loaded(false) {} _loaded(false) {
DeserializationError parse(VariantData &variant) { #ifdef ARDUINOJSON_DEBUG
DeserializationError err = parseVariant(variant); _ended = false;
#endif
}
template <typename TFilter>
DeserializationError parse(VariantData &variant, TFilter filter) {
DeserializationError err = parseVariant(variant, filter);
if (!err && _current != 0 && !variant.isEnclosed()) { if (!err && _current != 0 && !variant.isEnclosed()) {
// We don't detect trailing characters earlier, so we need to check now // We don't detect trailing characters earlier, so we need to check now
@ -44,7 +50,11 @@ class JsonDeserializer {
char current() { char current() {
if (!_loaded) { if (!_loaded) {
ARDUINOJSON_ASSERT(!_ended);
int c = _reader.read(); int c = _reader.read();
#ifdef ARDUINOJSON_DEBUG
if (c <= 0) _ended = true;
#endif
_current = static_cast<char>(c > 0 ? c : 0); _current = static_cast<char>(c > 0 ? c : 0);
_loaded = true; _loaded = true;
} }
@ -61,27 +71,61 @@ class JsonDeserializer {
return true; return true;
} }
DeserializationError parseVariant(VariantData &variant) { template <typename TFilter>
DeserializationError parseVariant(VariantData &variant, TFilter filter) {
DeserializationError err = skipSpacesAndComments(); DeserializationError err = skipSpacesAndComments();
if (err) return err; if (err) return err;
switch (current()) { switch (current()) {
case '[': case '[':
return parseArray(variant.toArray()); if (filter.allowArray())
return parseArray(variant.toArray(), filter);
else
return skipArray();
case '{': case '{':
return parseObject(variant.toObject()); if (filter.allowObject())
return parseObject(variant.toObject(), filter);
else
return skipObject();
case '\"': case '\"':
case '\'': case '\'':
return parseStringValue(variant); if (filter.allowValue())
return parseStringValue(variant);
else
return skipString();
default: default:
return parseNumericValue(variant); if (filter.allowValue())
return parseNumericValue(variant);
else
return skipNumericValue();
} }
} }
DeserializationError parseArray(CollectionData &array) { DeserializationError skipVariant() {
DeserializationError err = skipSpacesAndComments();
if (err) return err;
switch (current()) {
case '[':
return skipArray();
case '{':
return skipObject();
case '\"':
case '\'':
return skipString();
default:
return skipNumericValue();
}
}
template <typename TFilter>
DeserializationError parseArray(CollectionData &array, TFilter filter) {
if (_nestingLimit == 0) return DeserializationError::TooDeep; if (_nestingLimit == 0) return DeserializationError::TooDeep;
// Check opening braket // Check opening braket
@ -94,15 +138,48 @@ class JsonDeserializer {
// Empty array? // Empty array?
if (eat(']')) return DeserializationError::Ok; if (eat(']')) return DeserializationError::Ok;
TFilter memberFilter = filter[0UL];
// Read each value // Read each value
for (;;) { for (;;) {
// Allocate slot in array if (memberFilter.allow()) {
VariantData *value = array.add(_pool); // Allocate slot in array
if (!value) return DeserializationError::NoMemory; VariantData *value = array.add(_pool);
if (!value) return DeserializationError::NoMemory;
// 1 - Parse value // 1 - Parse value
_nestingLimit--;
err = parseVariant(*value, memberFilter);
_nestingLimit++;
if (err) return err;
} else {
_nestingLimit--;
err = skipVariant();
_nestingLimit++;
if (err) return err;
}
// 2 - Skip spaces
err = skipSpacesAndComments();
if (err) return err;
// 3 - More values?
if (eat(']')) return DeserializationError::Ok;
if (!eat(',')) return DeserializationError::InvalidInput;
}
}
DeserializationError skipArray() {
if (_nestingLimit == 0) return DeserializationError::TooDeep;
// Check opening braket
if (!eat('[')) return DeserializationError::InvalidInput;
// Read each value
for (;;) {
// 1 - Skip value
_nestingLimit--; _nestingLimit--;
err = parseVariant(*value); DeserializationError err = skipVariant();
_nestingLimit++; _nestingLimit++;
if (err) return err; if (err) return err;
@ -116,7 +193,8 @@ class JsonDeserializer {
} }
} }
DeserializationError parseObject(CollectionData &object) { template <typename TFilter>
DeserializationError parseObject(CollectionData &object, TFilter filter) {
if (_nestingLimit == 0) return DeserializationError::TooDeep; if (_nestingLimit == 0) return DeserializationError::TooDeep;
// Check opening brace // Check opening brace
@ -136,27 +214,37 @@ class JsonDeserializer {
err = parseKey(key); err = parseKey(key);
if (err) return err; if (err) return err;
VariantData *variant = object.get(adaptString(key));
if (!variant) {
// Allocate slot in object
VariantSlot *slot = object.addSlot(_pool);
if (!slot) return DeserializationError::NoMemory;
slot->setOwnedKey(make_not_null(key));
variant = slot->data();
}
// Skip spaces // Skip spaces
err = skipSpacesAndComments(); err = skipSpacesAndComments();
if (err) return err; // Colon if (err) return err; // Colon
if (!eat(':')) return DeserializationError::InvalidInput; if (!eat(':')) return DeserializationError::InvalidInput;
// Parse value TFilter memberFilter = filter[key];
_nestingLimit--;
err = parseVariant(*variant); if (memberFilter.allow()) {
_nestingLimit++; VariantData *variant = object.get(adaptString(key));
if (err) return err; if (!variant) {
// Allocate slot in object
VariantSlot *slot = object.addSlot(_pool);
if (!slot) return DeserializationError::NoMemory;
slot->setOwnedKey(make_not_null(key));
variant = slot->data();
}
// Parse value
_nestingLimit--;
err = parseVariant(*variant, memberFilter);
_nestingLimit++;
if (err) return err;
} else {
_stringStorage.reclaim(key);
_nestingLimit--;
err = skipVariant();
_nestingLimit++;
if (err) return err;
}
// Skip spaces // Skip spaces
err = skipSpacesAndComments(); err = skipSpacesAndComments();
@ -172,6 +260,46 @@ class JsonDeserializer {
} }
} }
DeserializationError skipObject() {
if (_nestingLimit == 0) return DeserializationError::TooDeep;
// Check opening brace
if (!eat('{')) return DeserializationError::InvalidInput;
// Skip spaces
DeserializationError err = skipSpacesAndComments();
if (err) return err;
// Empty object?
if (eat('}')) return DeserializationError::Ok;
// Read each key value pair
for (;;) {
// Skip key
err = skipVariant();
if (err) return err;
// Skip spaces
err = skipSpacesAndComments();
if (err) return err; // Colon
if (!eat(':')) return DeserializationError::InvalidInput;
// Skip value
_nestingLimit--;
err = skipVariant();
_nestingLimit++;
if (err) return err;
// Skip spaces
err = skipSpacesAndComments();
if (err) return err;
// More keys/values?
if (eat('}')) return DeserializationError::Ok;
if (!eat(',')) return DeserializationError::InvalidInput;
}
}
DeserializationError parseKey(const char *&key) { DeserializationError parseKey(const char *&key) {
if (isQuote(current())) { if (isQuote(current())) {
return parseQuotedString(key); return parseQuotedString(key);
@ -254,6 +382,21 @@ class JsonDeserializer {
return DeserializationError::Ok; return DeserializationError::Ok;
} }
DeserializationError skipString() {
const char stopChar = current();
move();
for (;;) {
char c = current();
move();
if (c == stopChar) break;
if (c == '\0') return DeserializationError::IncompleteInput;
if (c == '\\') _reader.read();
}
return DeserializationError::Ok;
}
DeserializationError parseNumericValue(VariantData &result) { DeserializationError parseNumericValue(VariantData &result) {
char buffer[64]; char buffer[64];
uint8_t n = 0; uint8_t n = 0;
@ -302,6 +445,15 @@ class JsonDeserializer {
return DeserializationError::InvalidInput; return DeserializationError::InvalidInput;
} }
DeserializationError skipNumericValue() {
char c = current();
while (c && c != '}' && c != ',' && c != ']' && c != ':') {
move();
c = current();
}
return DeserializationError::Ok;
}
DeserializationError parseHex4(uint16_t &result) { DeserializationError parseHex4(uint16_t &result) {
result = 0; result = 0;
for (uint8_t i = 0; i < 4; ++i) { for (uint8_t i = 0; i < 4; ++i) {
@ -401,33 +553,92 @@ class JsonDeserializer {
uint8_t _nestingLimit; uint8_t _nestingLimit;
char _current; char _current;
bool _loaded; bool _loaded;
#ifdef ARDUINOJSON_DEBUG
bool _ended;
#endif
}; };
// deserializeJson(JsonDocument&, const std::string&, ...)
template <typename TInput> template <typename TInput>
DeserializationError deserializeJson( DeserializationError deserializeJson(
JsonDocument &doc, const TInput &input, JsonDocument &doc, const TInput &input,
NestingLimit nestingLimit = NestingLimit()) { NestingLimit nestingLimit = NestingLimit()) {
return deserialize<JsonDeserializer>(doc, input, nestingLimit); return deserialize<JsonDeserializer>(doc, input, nestingLimit,
AllowAllFilter());
} }
template <typename TInput> template <typename TInput>
DeserializationError deserializeJson( DeserializationError deserializeJson(
JsonDocument &doc, TInput *input, JsonDocument &doc, const TInput &input, Filter filter,
NestingLimit nestingLimit = NestingLimit()) { NestingLimit nestingLimit = NestingLimit()) {
return deserialize<JsonDeserializer>(doc, input, nestingLimit); return deserialize<JsonDeserializer>(doc, input, nestingLimit, filter);
} }
template <typename TInput> template <typename TInput>
DeserializationError deserializeJson( DeserializationError deserializeJson(JsonDocument &doc, const TInput &input,
JsonDocument &doc, TInput *input, size_t inputSize, NestingLimit nestingLimit, Filter filter) {
NestingLimit nestingLimit = NestingLimit()) { return deserialize<JsonDeserializer>(doc, input, nestingLimit, filter);
return deserialize<JsonDeserializer>(doc, input, inputSize, nestingLimit);
} }
// deserializeJson(JsonDocument&, const std::istream&, ...)
template <typename TInput> template <typename TInput>
DeserializationError deserializeJson( DeserializationError deserializeJson(
JsonDocument &doc, TInput &input, JsonDocument &doc, TInput &input,
NestingLimit nestingLimit = NestingLimit()) { NestingLimit nestingLimit = NestingLimit()) {
return deserialize<JsonDeserializer>(doc, input, nestingLimit); return deserialize<JsonDeserializer>(doc, input, nestingLimit,
AllowAllFilter());
} }
template <typename TInput>
DeserializationError deserializeJson(
JsonDocument &doc, TInput &input, Filter filter,
NestingLimit nestingLimit = NestingLimit()) {
return deserialize<JsonDeserializer>(doc, input, nestingLimit, filter);
}
template <typename TInput>
DeserializationError deserializeJson(JsonDocument &doc, TInput &input,
NestingLimit nestingLimit, Filter filter) {
return deserialize<JsonDeserializer>(doc, input, nestingLimit, filter);
}
// deserializeJson(JsonDocument&, char*, ...)
template <typename TChar>
DeserializationError deserializeJson(
JsonDocument &doc, TChar *input,
NestingLimit nestingLimit = NestingLimit()) {
return deserialize<JsonDeserializer>(doc, input, nestingLimit,
AllowAllFilter());
}
template <typename TChar>
DeserializationError deserializeJson(
JsonDocument &doc, TChar *input, Filter filter,
NestingLimit nestingLimit = NestingLimit()) {
return deserialize<JsonDeserializer>(doc, input, nestingLimit, filter);
}
template <typename TChar>
DeserializationError deserializeJson(JsonDocument &doc, TChar *input,
NestingLimit nestingLimit, Filter filter) {
return deserialize<JsonDeserializer>(doc, input, nestingLimit, filter);
}
// deserializeJson(JsonDocument&, char*, size_t, ...)
template <typename TChar>
DeserializationError deserializeJson(
JsonDocument &doc, TChar *input, size_t inputSize,
NestingLimit nestingLimit = NestingLimit()) {
return deserialize<JsonDeserializer>(doc, input, inputSize, nestingLimit,
AllowAllFilter());
}
template <typename TChar>
DeserializationError deserializeJson(
JsonDocument &doc, TChar *input, size_t inputSize, Filter filter,
NestingLimit nestingLimit = NestingLimit()) {
return deserialize<JsonDeserializer>(doc, input, inputSize, nestingLimit,
filter);
}
template <typename TChar>
DeserializationError deserializeJson(JsonDocument &doc, TChar *input,
size_t inputSize,
NestingLimit nestingLimit, Filter filter) {
return deserialize<JsonDeserializer>(doc, input, inputSize, nestingLimit,
filter);
}
} // namespace ARDUINOJSON_NAMESPACE } // namespace ARDUINOJSON_NAMESPACE

View File

@ -74,6 +74,10 @@ class MemoryPool {
checkInvariants(); checkInvariants();
} }
void reclaimLastString(const char* s) {
_left = const_cast<char*>(s);
}
void clear() { void clear() {
_left = _begin; _left = _begin;
_right = _end; _right = _end;

View File

@ -26,7 +26,9 @@ class MsgPackDeserializer {
_stringStorage(stringStorage), _stringStorage(stringStorage),
_nestingLimit(nestingLimit) {} _nestingLimit(nestingLimit) {}
DeserializationError parse(VariantData &variant) { // TODO: add support for filter
DeserializationError parse(VariantData &variant,
AllowAllFilter = AllowAllFilter()) {
uint8_t code; uint8_t code;
if (!readByte(code)) return DeserializationError::IncompleteInput; if (!readByte(code)) return DeserializationError::IncompleteInput;
@ -317,27 +319,31 @@ template <typename TInput>
DeserializationError deserializeMsgPack( DeserializationError deserializeMsgPack(
JsonDocument &doc, const TInput &input, JsonDocument &doc, const TInput &input,
NestingLimit nestingLimit = NestingLimit()) { NestingLimit nestingLimit = NestingLimit()) {
return deserialize<MsgPackDeserializer>(doc, input, nestingLimit); return deserialize<MsgPackDeserializer>(doc, input, nestingLimit,
AllowAllFilter());
} }
template <typename TInput> template <typename TInput>
DeserializationError deserializeMsgPack( DeserializationError deserializeMsgPack(
JsonDocument &doc, TInput *input, JsonDocument &doc, TInput *input,
NestingLimit nestingLimit = NestingLimit()) { NestingLimit nestingLimit = NestingLimit()) {
return deserialize<MsgPackDeserializer>(doc, input, nestingLimit); return deserialize<MsgPackDeserializer>(doc, input, nestingLimit,
AllowAllFilter());
} }
template <typename TInput> template <typename TInput>
DeserializationError deserializeMsgPack( DeserializationError deserializeMsgPack(
JsonDocument &doc, TInput *input, size_t inputSize, JsonDocument &doc, TInput *input, size_t inputSize,
NestingLimit nestingLimit = NestingLimit()) { NestingLimit nestingLimit = NestingLimit()) {
return deserialize<MsgPackDeserializer>(doc, input, inputSize, nestingLimit); return deserialize<MsgPackDeserializer>(doc, input, inputSize, nestingLimit,
AllowAllFilter());
} }
template <typename TInput> template <typename TInput>
DeserializationError deserializeMsgPack( DeserializationError deserializeMsgPack(
JsonDocument &doc, TInput &input, JsonDocument &doc, TInput &input,
NestingLimit nestingLimit = NestingLimit()) { NestingLimit nestingLimit = NestingLimit()) {
return deserialize<MsgPackDeserializer>(doc, input, nestingLimit); return deserialize<MsgPackDeserializer>(doc, input, nestingLimit,
AllowAllFilter());
} }
} // namespace ARDUINOJSON_NAMESPACE } // namespace ARDUINOJSON_NAMESPACE

View File

@ -19,6 +19,10 @@ class StringCopier {
return StringBuilder(_pool); return StringBuilder(_pool);
} }
void reclaim(const char* s) {
_pool->reclaimLastString(s);
}
private: private:
MemoryPool* _pool; MemoryPool* _pool;
}; };

View File

@ -34,6 +34,11 @@ class StringMover {
return StringBuilder(&_ptr); return StringBuilder(&_ptr);
} }
// recover memory from last string
void reclaim(const char* str) {
_ptr = const_cast<char*>(str);
}
private: private:
char* _ptr; char* _ptr;
}; };

View File

@ -181,7 +181,7 @@ class VariantData {
} }
bool isEnclosed() const { bool isEnclosed() const {
return isCollection() || isString(); return !isFloat();
} }
void remove(size_t index) { void remove(size_t index) {