mirror of
				https://github.com/eledio-devices/thirdparty-ArduinoJson.git
				synced 2025-10-31 00:32:37 +01:00 
			
		
		
		
	Added DeserializationOption::Filter (closes #959)
				
					
				
			This commit is contained in:
		| @@ -1,6 +1,12 @@ | ||||
| ArduinoJson: change log | ||||
| ======================= | ||||
|  | ||||
| HEAD | ||||
| ---- | ||||
|  | ||||
| * Added `DeserializationOption::Filter` (issue #959) | ||||
| * Added example `JsonFilterExample.ino` | ||||
|  | ||||
| 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 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 filters the input to keep only desired values | ||||
|     * Supports single quotes as a string delimiter | ||||
|     * Compatible with NDJSON and JSON Lines | ||||
| * [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 | ||||
| 	issue772.cpp | ||||
| 	round_trip.cpp | ||||
| 	openweathermap.cpp | ||||
| ) | ||||
|  | ||||
| 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_static.cpp | ||||
| 	DeserializationError.cpp | ||||
| 	filter.cpp | ||||
| 	incomplete_input.cpp | ||||
| 	input_types.cpp | ||||
| 	number.cpp | ||||
| 	invalid_input.cpp | ||||
| 	misc.cpp | ||||
| 	nestingLimit.cpp | ||||
| 	number.cpp | ||||
| 	object.cpp | ||||
| 	object_static.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; | ||||
|  | ||||
| namespace DeserializationOption { | ||||
| using ARDUINOJSON_NAMESPACE::Filter; | ||||
| using ARDUINOJSON_NAMESPACE::NestingLimit; | ||||
| } | ||||
| }  // namespace DeserializationOption | ||||
| }  // 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 | ||||
|  | ||||
| #include <ArduinoJson/Deserialization/DeserializationError.hpp> | ||||
| #include <ArduinoJson/Deserialization/Filter.hpp> | ||||
| #include <ArduinoJson/Deserialization/NestingLimit.hpp> | ||||
| #include <ArduinoJson/Deserialization/Reader.hpp> | ||||
| #include <ArduinoJson/StringStorage/StringStorage.hpp> | ||||
| @@ -19,47 +20,52 @@ TDeserializer<TReader, TWriter> makeDeserializer(MemoryPool &pool, | ||||
|   return TDeserializer<TReader, TWriter>(pool, reader, writer, nestingLimit); | ||||
| } | ||||
|  | ||||
| // deserialize(JsonDocument&, const std::string&); | ||||
| // deserialize(JsonDocument&, const String&); | ||||
| // deserialize(JsonDocument&, char*); | ||||
| // deserialize(JsonDocument&, const char*); | ||||
| // deserialize(JsonDocument&, const __FlashStringHelper*); | ||||
| template <template <typename, typename> class TDeserializer, typename TString> | ||||
| // deserialize(JsonDocument&, const std::string&, NestingLimit, Filter); | ||||
| // deserialize(JsonDocument&, const String&, NestingLimit, Filter); | ||||
| // deserialize(JsonDocument&, char*, NestingLimit, Filter); | ||||
| // deserialize(JsonDocument&, const char*, NestingLimit, Filter); | ||||
| // deserialize(JsonDocument&, const __FlashStringHelper*, NestingLimit, Filter); | ||||
| template <template <typename, typename> class TDeserializer, typename TString, | ||||
|           typename TFilter> | ||||
| typename enable_if<!is_array<TString>::value, DeserializationError>::type | ||||
| deserialize(JsonDocument &doc, const TString &input, | ||||
|             NestingLimit nestingLimit) { | ||||
| deserialize(JsonDocument &doc, const TString &input, NestingLimit nestingLimit, | ||||
|             TFilter filter) { | ||||
|   Reader<TString> reader(input); | ||||
|   doc.clear(); | ||||
|   return makeDeserializer<TDeserializer>( | ||||
|              doc.memoryPool(), reader, | ||||
|              makeStringStorage(doc.memoryPool(), input), nestingLimit.value) | ||||
|       .parse(doc.data()); | ||||
|       .parse(doc.data(), filter); | ||||
| } | ||||
| // | ||||
| // deserialize(JsonDocument&, char*, size_t); | ||||
| // deserialize(JsonDocument&, const char*, size_t); | ||||
| // deserialize(JsonDocument&, const __FlashStringHelper*, size_t); | ||||
| template <template <typename, typename> class TDeserializer, typename TChar> | ||||
| // deserialize(JsonDocument&, char*, size_t, NestingLimit, Filter); | ||||
| // deserialize(JsonDocument&, const char*, size_t, NestingLimit, Filter); | ||||
| // deserialize(JsonDocument&, const __FlashStringHelper*, size_t, NL, Filter); | ||||
| template <template <typename, typename> class TDeserializer, typename TChar, | ||||
|           typename TFilter> | ||||
| DeserializationError deserialize(JsonDocument &doc, TChar *input, | ||||
|                                  size_t inputSize, NestingLimit nestingLimit) { | ||||
|                                  size_t inputSize, NestingLimit nestingLimit, | ||||
|                                  TFilter filter) { | ||||
|   BoundedReader<TChar *> reader(input, inputSize); | ||||
|   doc.clear(); | ||||
|   return makeDeserializer<TDeserializer>( | ||||
|              doc.memoryPool(), reader, | ||||
|              makeStringStorage(doc.memoryPool(), input), nestingLimit.value) | ||||
|       .parse(doc.data()); | ||||
|       .parse(doc.data(), filter); | ||||
| } | ||||
| // | ||||
| // deserialize(JsonDocument&, std::istream&); | ||||
| // deserialize(JsonDocument&, Stream&); | ||||
| template <template <typename, typename> class TDeserializer, typename TStream> | ||||
| // deserialize(JsonDocument&, std::istream&, NestingLimit, Filter); | ||||
| // deserialize(JsonDocument&, Stream&, NestingLimit, Filter); | ||||
| template <template <typename, typename> class TDeserializer, typename TStream, | ||||
|           typename TFilter> | ||||
| DeserializationError deserialize(JsonDocument &doc, TStream &input, | ||||
|                                  NestingLimit nestingLimit) { | ||||
|                                  NestingLimit nestingLimit, TFilter filter) { | ||||
|   Reader<TStream> reader(input); | ||||
|   doc.clear(); | ||||
|   return makeDeserializer<TDeserializer>( | ||||
|              doc.memoryPool(), reader, | ||||
|              makeStringStorage(doc.memoryPool(), input), nestingLimit.value) | ||||
|       .parse(doc.data()); | ||||
|       .parse(doc.data(), filter); | ||||
| } | ||||
|  | ||||
| }  // namespace ARDUINOJSON_NAMESPACE | ||||
|   | ||||
| @@ -27,9 +27,15 @@ class JsonDeserializer { | ||||
|         _reader(reader), | ||||
|         _stringStorage(stringStorage), | ||||
|         _nestingLimit(nestingLimit), | ||||
|         _loaded(false) {} | ||||
|   DeserializationError parse(VariantData &variant) { | ||||
|     DeserializationError err = parseVariant(variant); | ||||
|         _loaded(false) { | ||||
| #ifdef ARDUINOJSON_DEBUG | ||||
|     _ended = false; | ||||
| #endif | ||||
|   } | ||||
|  | ||||
|   template <typename TFilter> | ||||
|   DeserializationError parse(VariantData &variant, TFilter filter) { | ||||
|     DeserializationError err = parseVariant(variant, filter); | ||||
|  | ||||
|     if (!err && _current != 0 && !variant.isEnclosed()) { | ||||
|       // We don't detect trailing characters earlier, so we need to check now | ||||
| @@ -44,7 +50,11 @@ class JsonDeserializer { | ||||
|  | ||||
|   char current() { | ||||
|     if (!_loaded) { | ||||
|       ARDUINOJSON_ASSERT(!_ended); | ||||
|       int c = _reader.read(); | ||||
| #ifdef ARDUINOJSON_DEBUG | ||||
|       if (c <= 0) _ended = true; | ||||
| #endif | ||||
|       _current = static_cast<char>(c > 0 ? c : 0); | ||||
|       _loaded = true; | ||||
|     } | ||||
| @@ -61,27 +71,61 @@ class JsonDeserializer { | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   DeserializationError parseVariant(VariantData &variant) { | ||||
|   template <typename TFilter> | ||||
|   DeserializationError parseVariant(VariantData &variant, TFilter filter) { | ||||
|     DeserializationError err = skipSpacesAndComments(); | ||||
|     if (err) return err; | ||||
|  | ||||
|     switch (current()) { | ||||
|       case '[': | ||||
|         return parseArray(variant.toArray()); | ||||
|         if (filter.allowArray()) | ||||
|           return parseArray(variant.toArray(), filter); | ||||
|         else | ||||
|           return skipArray(); | ||||
|  | ||||
|       case '{': | ||||
|         return parseObject(variant.toObject()); | ||||
|         if (filter.allowObject()) | ||||
|           return parseObject(variant.toObject(), filter); | ||||
|         else | ||||
|           return skipObject(); | ||||
|  | ||||
|       case '\"': | ||||
|       case '\'': | ||||
|         return parseStringValue(variant); | ||||
|         if (filter.allowValue()) | ||||
|           return parseStringValue(variant); | ||||
|         else | ||||
|           return skipString(); | ||||
|  | ||||
|       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; | ||||
|  | ||||
|     // Check opening braket | ||||
| @@ -94,15 +138,48 @@ class JsonDeserializer { | ||||
|     // Empty array? | ||||
|     if (eat(']')) return DeserializationError::Ok; | ||||
|  | ||||
|     TFilter memberFilter = filter[0UL]; | ||||
|  | ||||
|     // Read each value | ||||
|     for (;;) { | ||||
|       // Allocate slot in array | ||||
|       VariantData *value = array.add(_pool); | ||||
|       if (!value) return DeserializationError::NoMemory; | ||||
|       if (memberFilter.allow()) { | ||||
|         // Allocate slot in array | ||||
|         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--; | ||||
|       err = parseVariant(*value); | ||||
|       DeserializationError err = skipVariant(); | ||||
|       _nestingLimit++; | ||||
|       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; | ||||
|  | ||||
|     // Check opening brace | ||||
| @@ -136,27 +214,37 @@ class JsonDeserializer { | ||||
|       err = parseKey(key); | ||||
|       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 | ||||
|       err = skipSpacesAndComments(); | ||||
|       if (err) return err;  // Colon | ||||
|       if (!eat(':')) return DeserializationError::InvalidInput; | ||||
|  | ||||
|       // Parse value | ||||
|       _nestingLimit--; | ||||
|       err = parseVariant(*variant); | ||||
|       _nestingLimit++; | ||||
|       if (err) return err; | ||||
|       TFilter memberFilter = filter[key]; | ||||
|  | ||||
|       if (memberFilter.allow()) { | ||||
|         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(); | ||||
|         } | ||||
|  | ||||
|         // 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 | ||||
|       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) { | ||||
|     if (isQuote(current())) { | ||||
|       return parseQuotedString(key); | ||||
| @@ -254,6 +382,21 @@ class JsonDeserializer { | ||||
|     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) { | ||||
|     char buffer[64]; | ||||
|     uint8_t n = 0; | ||||
| @@ -302,6 +445,15 @@ class JsonDeserializer { | ||||
|     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) { | ||||
|     result = 0; | ||||
|     for (uint8_t i = 0; i < 4; ++i) { | ||||
| @@ -401,33 +553,92 @@ class JsonDeserializer { | ||||
|   uint8_t _nestingLimit; | ||||
|   char _current; | ||||
|   bool _loaded; | ||||
| #ifdef ARDUINOJSON_DEBUG | ||||
|   bool _ended; | ||||
| #endif | ||||
| }; | ||||
|  | ||||
| // deserializeJson(JsonDocument&, const std::string&, ...) | ||||
| template <typename TInput> | ||||
| DeserializationError deserializeJson( | ||||
|     JsonDocument &doc, const TInput &input, | ||||
|     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, | ||||
|     JsonDocument &doc, const TInput &input, Filter filter, | ||||
|     NestingLimit nestingLimit = NestingLimit()) { | ||||
|   return deserialize<JsonDeserializer>(doc, input, nestingLimit); | ||||
|   return deserialize<JsonDeserializer>(doc, input, nestingLimit, filter); | ||||
| } | ||||
|  | ||||
| template <typename TInput> | ||||
| DeserializationError deserializeJson( | ||||
|     JsonDocument &doc, TInput *input, size_t inputSize, | ||||
|     NestingLimit nestingLimit = NestingLimit()) { | ||||
|   return deserialize<JsonDeserializer>(doc, input, inputSize, nestingLimit); | ||||
| DeserializationError deserializeJson(JsonDocument &doc, const TInput &input, | ||||
|                                      NestingLimit nestingLimit, Filter filter) { | ||||
|   return deserialize<JsonDeserializer>(doc, input, nestingLimit, filter); | ||||
| } | ||||
|  | ||||
| // deserializeJson(JsonDocument&, const std::istream&, ...) | ||||
| template <typename TInput> | ||||
| DeserializationError deserializeJson( | ||||
|     JsonDocument &doc, TInput &input, | ||||
|     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 | ||||
|   | ||||
| @@ -74,6 +74,10 @@ class MemoryPool { | ||||
|     checkInvariants(); | ||||
|   } | ||||
|  | ||||
|   void reclaimLastString(const char* s) { | ||||
|     _left = const_cast<char*>(s); | ||||
|   } | ||||
|  | ||||
|   void clear() { | ||||
|     _left = _begin; | ||||
|     _right = _end; | ||||
|   | ||||
| @@ -26,7 +26,9 @@ class MsgPackDeserializer { | ||||
|         _stringStorage(stringStorage), | ||||
|         _nestingLimit(nestingLimit) {} | ||||
|  | ||||
|   DeserializationError parse(VariantData &variant) { | ||||
|   // TODO: add support for filter | ||||
|   DeserializationError parse(VariantData &variant, | ||||
|                              AllowAllFilter = AllowAllFilter()) { | ||||
|     uint8_t code; | ||||
|     if (!readByte(code)) return DeserializationError::IncompleteInput; | ||||
|  | ||||
| @@ -317,27 +319,31 @@ template <typename TInput> | ||||
| DeserializationError deserializeMsgPack( | ||||
|     JsonDocument &doc, const TInput &input, | ||||
|     NestingLimit nestingLimit = NestingLimit()) { | ||||
|   return deserialize<MsgPackDeserializer>(doc, input, nestingLimit); | ||||
|   return deserialize<MsgPackDeserializer>(doc, input, nestingLimit, | ||||
|                                           AllowAllFilter()); | ||||
| } | ||||
|  | ||||
| template <typename TInput> | ||||
| DeserializationError deserializeMsgPack( | ||||
|     JsonDocument &doc, TInput *input, | ||||
|     NestingLimit nestingLimit = NestingLimit()) { | ||||
|   return deserialize<MsgPackDeserializer>(doc, input, nestingLimit); | ||||
|   return deserialize<MsgPackDeserializer>(doc, input, nestingLimit, | ||||
|                                           AllowAllFilter()); | ||||
| } | ||||
|  | ||||
| template <typename TInput> | ||||
| DeserializationError deserializeMsgPack( | ||||
|     JsonDocument &doc, TInput *input, size_t inputSize, | ||||
|     NestingLimit nestingLimit = NestingLimit()) { | ||||
|   return deserialize<MsgPackDeserializer>(doc, input, inputSize, nestingLimit); | ||||
|   return deserialize<MsgPackDeserializer>(doc, input, inputSize, nestingLimit, | ||||
|                                           AllowAllFilter()); | ||||
| } | ||||
|  | ||||
| template <typename TInput> | ||||
| DeserializationError deserializeMsgPack( | ||||
|     JsonDocument &doc, TInput &input, | ||||
|     NestingLimit nestingLimit = NestingLimit()) { | ||||
|   return deserialize<MsgPackDeserializer>(doc, input, nestingLimit); | ||||
|   return deserialize<MsgPackDeserializer>(doc, input, nestingLimit, | ||||
|                                           AllowAllFilter()); | ||||
| } | ||||
| }  // namespace ARDUINOJSON_NAMESPACE | ||||
|   | ||||
| @@ -19,6 +19,10 @@ class StringCopier { | ||||
|     return StringBuilder(_pool); | ||||
|   } | ||||
|  | ||||
|   void reclaim(const char* s) { | ||||
|     _pool->reclaimLastString(s); | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   MemoryPool* _pool; | ||||
| }; | ||||
|   | ||||
| @@ -34,6 +34,11 @@ class StringMover { | ||||
|     return StringBuilder(&_ptr); | ||||
|   } | ||||
|  | ||||
|   // recover memory from last string | ||||
|   void reclaim(const char* str) { | ||||
|     _ptr = const_cast<char*>(str); | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   char* _ptr; | ||||
| }; | ||||
|   | ||||
| @@ -181,7 +181,7 @@ class VariantData { | ||||
|   } | ||||
|  | ||||
|   bool isEnclosed() const { | ||||
|     return isCollection() || isString(); | ||||
|     return !isFloat(); | ||||
|   } | ||||
|  | ||||
|   void remove(size_t index) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user