mirror of
				https://github.com/eledio-devices/thirdparty-ArduinoJson.git
				synced 2025-10-31 08:42:39 +01: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