mirror of
https://github.com/bblanchon/ArduinoJson.git
synced 2025-07-18 13:02:25 +02:00
Added DeserializationOption::Filter
(closes #959)
This commit is contained in:
@ -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)
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
@ -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/)
|
||||||
|
66
examples/JsonFilterExample/JsonFilterExample.ino
Normal file
66
examples/JsonFilterExample/JsonFilterExample.ino
Normal 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 ❤❤❤❤❤
|
@ -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")
|
||||||
|
71
extras/tests/IntegrationTests/openweathermap.cpp
Normal file
71
extras/tests/IntegrationTests/openweathermap.cpp
Normal file
File diff suppressed because one or more lines are too long
@ -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
|
||||||
|
577
extras/tests/JsonDeserializer/filter.cpp
Normal file
577
extras/tests/JsonDeserializer/filter.cpp
Normal 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);
|
||||||
|
}
|
@ -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
|
||||||
|
66
src/ArduinoJson/Deserialization/Filter.hpp
Normal file
66
src/ArduinoJson/Deserialization/Filter.hpp
Normal 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
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -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) {
|
||||||
|
Reference in New Issue
Block a user