diff --git a/examples/ReadWriteConfigFile/ReadWriteConfigFile.ino b/examples/ReadWriteConfigFile/ReadWriteConfigFile.ino index 631de62..30bd24b 100644 --- a/examples/ReadWriteConfigFile/ReadWriteConfigFile.ino +++ b/examples/ReadWriteConfigFile/ReadWriteConfigFile.ino @@ -5,10 +5,10 @@ const char* yaml_example_str = R"_YAML_STRING_( -my_setting: "false" +my_setting: false -flag1: "true" -flag2: "true" +flag1: true +flag2: true settings1: just_a_string: "I am a string" @@ -59,7 +59,7 @@ bool loadYamlConfig() return false; } myConfig = json_doc.as(); - current_value = myConfig[nodename].as() == "true"; + current_value = myConfig[nodename].as(); //serializeJson( myConfig, Serial ); //Serial.println(); return true; @@ -86,8 +86,7 @@ bool toggleYamlProperty() //Serial.printf("Initial value: [%s] = %s\n", nodename, current_value ? "true" : "false" ); current_value = !current_value; Serial.printf("New value: [%s] = %s\n", nodename, current_value ? "true" : "false" ); - // ArduinoJson @bool is borked up so we use a string - myConfig[nodename] = current_value ? "true" : "false"; + myConfig[nodename] = current_value; return saveYamlConfig(); } diff --git a/examples/test/src/test.cpp b/examples/test/src/test.cpp index 864eb98..4b40af2 100644 --- a/examples/test/src/test.cpp +++ b/examples/test/src/test.cpp @@ -5,7 +5,8 @@ // sorry about the notation, but it looks nicer than chunk-splitting+quoting const char* yaml_example_str = R"_YAML_STRING_( -first: "true" +first: true +fourth: false blah: just_a_string: "I am a string" array_of_strings: @@ -28,14 +29,15 @@ first: "true" inline_json_for_the_haters: { "hello":"json", "nested":[3,2,"1","moon"] } whatever: nope: ["n","o","p","e"] -last: "true" +last: true )_YAML_STRING_"; const char* json_example_str = R"_JSON_STRING_( { - "first": "true", + "first": true, + "fourth": false, "blah": { "just_a_string": "I am a string", "array_of_strings": ["oops", "meh" ], @@ -59,35 +61,22 @@ const char* json_example_str = R"_JSON_STRING_( "inline_json_for_the_haters": { "hello": "json", "nested": [ 3, 2, "1", "moon" ] } }, "whatever": { "nope": [ "n", "o", "p", "e" ] }, - "last": "true" + "last": true } )_JSON_STRING_"; -void setup() -{ - Serial.begin(115200); - delay(5000); - Serial.print("Welcome to the YAML Test sketch\nRam free: "); - Serial.print( HEAP_AVAILABLE() ); - Serial.println(" bytes"); - - const size_t yaml_str_size = strlen(yaml_example_str); - const size_t json_str_size = strlen(json_example_str); - int test_number = 1; +const size_t yaml_str_size = strlen(yaml_example_str); +const size_t json_str_size = strlen(json_example_str); +int test_number = 1; - Serial.printf("[DEBUG] YAML (in):\n%s\n\n", yaml_example_str); - YAMLParser::setLogLevel( YAML::LogLevelDebug ); // override sketch debug level (otherwise inherited) +#if defined HAS_ARDUINOJSON - #if defined HAS_ARDUINOJSON - #pragma message "Enabling ArduinoJson tests" - - YAML_LOG_n("YAML=>JSON and JSON=>YAML using ArduinoJson\n\n"); - + void test_deserializeYml_JsonDocument_YamlStream() + { #if !defined ARDUINO_ARCH_SAMD // samd with 32kb ram can oom after this, so only enable for specific test - { YAML_LOG_n( "[TEST #%d] YAML stream to JsonObject -> deserializeYml(json_doc, yaml_stream):", test_number++ ); DynamicJsonDocument json_doc(yaml_str_size*2); String yaml_str = String( yaml_example_str ); @@ -99,167 +88,208 @@ void setup() } const size_t bytes_out = serializeJsonPretty( json_doc, Serial ); // print deserialized JsonObject YAML_LOG_n("[YAML=>JsonObject] yaml bytes in=%d, json bytes out=%d\n\n", yaml_str_size, bytes_out); - } #endif - - - { - YAML_LOG_n( "[TEST #%d] YAML string to JsonObject -> deserializeYml(json_doc, yaml_example_str):", test_number++ ); - DynamicJsonDocument json_doc(yaml_str_size*2); - auto err = deserializeYml( json_doc, yaml_example_str ); // deserialize yaml string to JsonDocument - if( err ) { - YAML_LOG_n("Unable to deserialize demo YAML to JsonObject: %s", err.c_str() ); - return; - } - JsonObject json_obj = json_doc.as(); - const size_t bytes_out = serializeJsonPretty( json_obj, Serial ); // print deserialized JsonObject - YAML_LOG_n("[YAML=>JsonObject] yaml bytes in=%d, json bytes out=%d\n\n", yaml_str_size, bytes_out); + } + + + void test_deserializeYml_JsonDocument_YamlString() + { + YAML_LOG_n( "[TEST #%d] YAML string to JsonObject -> deserializeYml(json_doc, yaml_example_str):", test_number++ ); + DynamicJsonDocument json_doc(yaml_str_size*2); + String yaml_str( yaml_example_str ); + auto err = deserializeYml( json_doc, yaml_str.c_str() ); // deserialize yaml string to JsonDocument + if( err ) { + YAML_LOG_n("Unable to deserialize demo YAML to JsonObject: %s", err.c_str() ); + return; } - - - - - - { - // Convert JsonObject to yaml - YAML_LOG_n( "[TEST #%d] JsonObject to YAML stream -> serializeYml(json_obj, yaml_stream_out):", test_number++ ); - String str_yaml_out = ""; // YAML output string - StringStream yaml_stream_out( str_yaml_out ); // Stream to str_yaml_out - // create and populate a JsonObject - DynamicJsonDocument doc(json_str_size*2); - auto err = deserializeJson( doc, json_example_str ); - if( err ) { - YAML_LOG_n("Unable to deserialize demo JSON to JsonObject: %s", err.c_str() ); - return; - } - JsonObject json_obj = doc.as(); - const size_t bytes_out = serializeYml( json_obj, yaml_stream_out ); - Serial.println( str_yaml_out ); - YAML_LOG_n("[JsonObject=>YAML] json bytes in=%d, yaml bytes out=%d\n\n", json_str_size, bytes_out ); + JsonObject json_obj = json_doc.as(); + const size_t bytes_out = serializeJsonPretty( json_obj, Serial ); // print deserialized JsonObject + YAML_LOG_n("[YAML=>JsonObject] yaml bytes in=%d, json bytes out=%d\n\n", yaml_str_size, bytes_out); + } + + + void test_serializeYml_JsonObject_YamlStream() + { + // Convert JsonObject to yaml + YAML_LOG_n( "[TEST #%d] JsonObject to YAML stream -> serializeYml(json_obj, yaml_stream_out):", test_number++ ); + String str_yaml_out = ""; // YAML output string + String json_str = String( json_example_str ); + StringStream yaml_stream_out( str_yaml_out ); // Stream to str_yaml_out + // create and populate a JsonObject + DynamicJsonDocument doc(json_str_size*2); + auto err = deserializeJson( doc, json_str.c_str() ); + if( err ) { + YAML_LOG_n("Unable to deserialize demo JSON to JsonObject: %s", err.c_str() ); + return; } + JsonObject json_obj = doc.as(); + const size_t bytes_out = serializeYml( json_obj, yaml_stream_out ); + Serial.println( str_yaml_out ); + YAML_LOG_n("[JsonObject=>YAML] json bytes in=%d, yaml bytes out=%d\n\n", json_str_size, bytes_out ); + } + + + void test_serializeYml_JsonObject_YamlString() + { + // Convert JsonObject to yaml + YAML_LOG_n( "[TEST #%d] JsonObject to YAML stream -> serializeYml(json_obj, str_yaml_out):", test_number++ ); + String str_yaml_out = ""; // YAML output string + String json_str = String( json_example_str ); + //StringStream yaml_stream_out( str_yaml_out ); // Stream to str_yaml_out + // create and populate a JsonObject + DynamicJsonDocument doc(json_str_size*2); + auto err = deserializeJson( doc, json_str.c_str() ); + if( err ) { + YAML_LOG_n("Unable to deserialize demo JSON to JsonObject: %s", err.c_str() ); + return; + } + JsonObject json_obj = doc.as(); + const size_t bytes_out = serializeYml( json_obj, str_yaml_out ); + Serial.println( str_yaml_out ); + YAML_LOG_n("[JsonObject=>YAML] json bytes in=%d, yaml bytes out=%d\n\n", json_str_size, bytes_out ); + } +#endif // defined HAS_ARDUINOJSON - { - // Convert JsonObject to yaml - YAML_LOG_n( "[TEST #%d] JsonObject to YAML stream -> serializeYml(json_obj, str_yaml_out):", test_number++ ); - String str_yaml_out = ""; // YAML output string - //StringStream yaml_stream_out( str_yaml_out ); // Stream to str_yaml_out - // create and populate a JsonObject - DynamicJsonDocument doc(json_str_size*2); - auto err = deserializeJson( doc, json_example_str ); - if( err ) { - YAML_LOG_n("Unable to deserialize demo JSON to JsonObject: %s", err.c_str() ); - return; - } - JsonObject json_obj = doc.as(); - const size_t bytes_out = serializeYml( json_obj, str_yaml_out ); - Serial.println( str_yaml_out ); - YAML_LOG_n("[JsonObject=>YAML] json bytes in=%d, yaml bytes out=%d\n\n", json_str_size, bytes_out ); - } - #if defined USE_STREAM_TO_STREAM // stream to stream unavailable on esp8266 (not enough memory) +#if defined USE_STREAM_TO_STREAM // stream to stream unavailable on low memory devices + + void test_serializeYml_JsonStream_YamlStream() + { #pragma message "Enabling ArduinoJson stream<=>stream tests" { - YAML_LOG_n( "[TEST #%d] JSON stream to JsonObject to YAML stream -> serializeYml(stream_in, Serial):", test_number++ ); + YAML_LOG_n( "[TEST #%d] JSON stream to JsonObject to YAML stream -> serializeYml(stream_in, stream_out):", test_number++ ); + String str_yaml = ""; String str_json = String( json_example_str ); StringStream stream_in( str_json ); - const size_t bytes_out = serializeYml( stream_in, Serial ); + StringStream stream_out( str_yaml ); + const size_t bytes_out = serializeYml( stream_in, stream_out ); + Serial.println( str_yaml ); YAML_LOG_n("[JSON=>JsonObject=>YAML] json bytes in=%d, yaml bytes out=%d\n", json_str_size, bytes_out); } - #endif + } +#endif - YAML_LOG_n("ArduinoJson tests complete"); - - #endif +#if defined HAS_CJSON + void test_deserializeYml_cJson_String() + { + YAML_LOG_n( "YAML string to cJSON Object -> deserializeYml(cJSON_obj*, yaml_example_str):" ); + // deserialize YAML string into cJSON object + cJSON* objPtr = (cJSON*)malloc( sizeof(cJSON) ); // allocate minimal memory to empty object + int ret = deserializeYml( objPtr, yaml_example_str ); + if (!ret) { + Serial.println("deserializeYml failed"); + return; + } + YAML_LOG_n("Printing json"); + char* json = cJSON_Print( objPtr ); + if( !json ) { + YAML_LOG_e("emtpy output, aborting"); + return; + } + String str_json_out = String( json ); + free(json); + Serial.print( str_json_out ); + cJSON_Delete( objPtr ); + YAML_LOG_n("[YAML=>cJsonObject] yaml bytes in=%d, json bytes out=%d\n", yaml_str_size, str_json_out.length() ); + } + + + void test_deserializeYml_cJson_Stream() + { + YAML_LOG_n( "YAML stream to cJSON Object -> deserializeYml(cJSON_obj*, yaml_stream):" ); + String yaml_str = String( yaml_example_str ); + StringStream yaml_stream( yaml_str ); + cJSON* objPtr = (cJSON*)malloc( sizeof(cJSON) ); // allocate minimal memory to empty object + // deserialize YAML stream into cJSON object + int ret = deserializeYml( objPtr, yaml_stream ); + if (!ret) { + Serial.println("deserializeYml failed"); + return; + } + YAML_LOG_n("Printing json"); + char* json = cJSON_Print( objPtr ); + if( !json ) { + YAML_LOG_e("emtpy output, aborting"); + return; + } + size_t bytes_out = strlen(json); + Serial.print( json ); + free(json); + cJSON_Delete( objPtr ); + YAML_LOG_n("[YAML=>cJsonObject] yaml bytes in=%d, json bytes out=%d\n", yaml_str.length(), bytes_out ); + } - #if __has_include() - #pragma message "Enabling cJSON tests" + void test_serializeYml_cJson_Stream() + { + YAML_LOG_n( "cJSON Object to YAML stream -> serializeYml( objPtr, Serial ):" ); + cJSON* objPtr = cJSON_Parse( json_example_str ); + size_t bytes_out = serializeYml( objPtr, Serial ); + cJSON_Delete( objPtr ); + YAML_LOG_n("[YAML=>cJsonObject=>YAML] yaml bytes in=%d, json bytes out=%d\n", json_str_size, bytes_out); + } - YAML_LOG_n("\n\nYAML=>JSON and JSON=>YAML using cJSON:\n"); + void test_serializeYml_cJson_String() + { + YAML_LOG_n( "cJSON Object to YAML string -> serializeYml( objPtr, yaml_dest_str ):" ); + cJSON* objPtr = cJSON_Parse( json_example_str ); + String yaml_dest_str; + size_t bytes_out = serializeYml( objPtr, yaml_dest_str ); + Serial.println( yaml_dest_str ); + cJSON_Delete( objPtr ); + YAML_LOG_n("[YAML=>cJsonObject=>YAML] yaml bytes in=%d, json bytes out=%d\n", json_str_size, bytes_out ); + } +#endif - { - YAML_LOG_n( "YAML string to cJSON Object -> deserializeYml(cJSON_obj*, yaml_example_str):" ); - // deserialize YAML string into cJSON object - cJSON* objPtr = (cJSON*)malloc( sizeof(cJSON) ); // allocate minimal memory to empty object - int ret = deserializeYml( objPtr, yaml_example_str ); - if (!ret) { - Serial.println("deserializeYml failed"); - return; - } - YAML_LOG_n("Printing json"); - char* json = cJSON_Print( objPtr ); - if( !json ) { - YAML_LOG_e("emtpy output, aborting"); - return; - } - String str_json_out = String( json ); - free(json); - Serial.print( str_json_out ); - cJSON_Delete( objPtr ); - YAML_LOG_n("[YAML=>cJsonObject] yaml bytes in=%d, json bytes out=%d\n", yaml_str_size, str_json_out.length() ); - } - { - YAML_LOG_n( "YAML stream to cJSON Object -> deserializeYml(cJSON_obj*, yaml_stream):" ); - String yaml_str = String( yaml_example_str ); - StringStream yaml_stream( yaml_str ); - cJSON* objPtr = (cJSON*)malloc( sizeof(cJSON) ); // allocate minimal memory to empty object - // deserialize YAML stream into cJSON object - int ret = deserializeYml( objPtr, yaml_stream ); - if (!ret) { - Serial.println("deserializeYml failed"); - return; - } - YAML_LOG_n("Printing json"); - char* json = cJSON_Print( objPtr ); - if( !json ) { - YAML_LOG_e("emtpy output, aborting"); - return; - } - size_t bytes_out = strlen(json); - Serial.print( json ); - free(json); - cJSON_Delete( objPtr ); - YAML_LOG_n("[YAML=>cJsonObject] yaml bytes in=%d, json bytes out=%d\n", yaml_str.length(), bytes_out ); - } +void setup() +{ + Serial.begin(115200); + delay(5000); + Serial.print("Welcome to the YAML Test sketch\nRam free: "); + Serial.print( HEAP_AVAILABLE() ); + Serial.println(" bytes"); + Serial.printf("[DEBUG] YAML (in):\n%s\n\n", yaml_example_str); - { - YAML_LOG_n( "cJSON Object to YAML stream -> serializeYml( objPtr, Serial ):" ); - cJSON* objPtr = cJSON_Parse( json_example_str ); - size_t bytes_out = serializeYml( objPtr, Serial ); - cJSON_Delete( objPtr ); - YAML_LOG_n("[YAML=>cJsonObject=>YAML] yaml bytes in=%d, json bytes out=%d\n", json_str_size, bytes_out); - } + YAMLParser::setLogLevel( YAML::LogLevelDebug ); // override sketch debug level (otherwise inherited) + #if defined HAS_ARDUINOJSON + #pragma message "Enabling ArduinoJson tests" + YAML_LOG_n("YAML=>JSON and JSON=>YAML using ArduinoJson\n\n"); + //test_deserializeYml_JsonObject_YamlStream(); + //test_deserializeYml_JsonObject_YamlString(); + test_deserializeYml_JsonDocument_YamlStream(); + test_deserializeYml_JsonDocument_YamlString(); + test_serializeYml_JsonObject_YamlStream(); + test_serializeYml_JsonObject_YamlString(); + YAML_LOG_n("ArduinoJson tests complete"); + #endif - { - YAML_LOG_n( "cJSON Object to YAML string -> serializeYml( objPtr, yaml_dest_str ):" ); - cJSON* objPtr = cJSON_Parse( json_example_str ); - String yaml_dest_str; - size_t bytes_out = serializeYml( objPtr, yaml_dest_str ); - Serial.println( yaml_dest_str ); - cJSON_Delete( objPtr ); - YAML_LOG_n("[YAML=>cJsonObject=>YAML] yaml bytes in=%d, json bytes out=%d\n", json_str_size, bytes_out ); - } + #if defined USE_STREAM_TO_STREAM // stream to stream unavailable on esp8266 (not enough memory) + test_serializeYml_JsonStream_YamlStream(); + #endif + #if defined HAS_CJSON + #pragma message "Enabling cJSON tests" + YAML_LOG_n("\n\nYAML=>JSON and JSON=>YAML using cJSON:\n"); + test_deserializeYml_cJson_String(); + test_deserializeYml_cJson_Stream(); + test_serializeYml_cJson_Stream(); + test_serializeYml_cJson_String(); YAML_LOG_n("cJSON tests complete"); - - #endif - - } diff --git a/library.properties b/library.properties index 0fa3a59..a0faa98 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=YAMLDuino -version=1.2.4 +version=1.2.5 author=tobozo maintainer=tobozo sentence=A simple and efficient YAML library for embedded C++ diff --git a/src/ArduinoYaml.cpp b/src/ArduinoYaml.cpp index fcdf283..c656484 100644 --- a/src/ArduinoYaml.cpp +++ b/src/ArduinoYaml.cpp @@ -30,6 +30,71 @@ #include "ArduinoYaml.hpp" + +String _indent_str; +const char* indent( size_t size ) +{ + _indent_str = ""; + for( size_t i=0;idata.scalar.style) { + case YAML_PLAIN_SCALAR_STYLE: + { + String _scalar = String( (char*)yamlNode->data.scalar.value ); + if( string_has_bool_value(_scalar, value_out) ) { + return true; + } + } + break; + case YAML_SINGLE_QUOTED_SCALAR_STYLE: /*YAML_LOG_e(" '")*/; break; + case YAML_DOUBLE_QUOTED_SCALAR_STYLE: /*YAML_LOG_e(" \"")*/; break; + case YAML_LITERAL_SCALAR_STYLE: /*YAML_LOG_e(" |")*/; break; + case YAML_FOLDED_SCALAR_STYLE: /*YAML_LOG_e(" >")*/; break; + case YAML_ANY_SCALAR_STYLE: /*abort(); */ break; + } + return false; + +} + + // used for internals, mainly because yaml_emitter_dump() destroys data after emitting int yaml_copy_document(yaml_document_t *dest, yaml_document_t *src) { @@ -109,9 +174,7 @@ YAMLParser::~YAMLParser() { yaml_parser_delete(&_parser); yaml_emitter_delete(&_emitter); - #if !defined ESP8266 // TODO: fix mem leak with esp8266 - yaml_document_delete(&_document); - #endif + yaml_document_delete(&_document); } @@ -163,7 +226,9 @@ void YAMLParser::loadDocument() { yaml_document_t document; assert(yaml_emitter_initialize(&_emitter)); - yaml_stream_handler_data_t shd = { &_yaml_stream, &_bytes_written }; + yaml_stream_handler_data_t shd = { _yaml_stream, &_bytes_written }; + yaml_emitter_set_canonical(&_emitter, 1); + yaml_emitter_set_unicode(&_emitter, 1); yaml_emitter_set_output(&_emitter, &_yaml_stream_writer, &shd); yaml_emitter_open(&_emitter); if (!yaml_parser_load(&_parser, &document)) { @@ -174,8 +239,10 @@ void YAMLParser::loadDocument() assert( yaml_copy_document(&_document, &document) ); // copy into local document for later parsing assert( yaml_emitter_dump(&_emitter, &document) ); // dump to emitter for output length evaluation _emitter_delete: + yaml_parser_delete(&_parser); yaml_emitter_close(&_emitter); yaml_emitter_delete(&_emitter); + yaml_document_delete(&document); } @@ -219,6 +286,7 @@ void YAMLParser::handle_emitter_error(yaml_emitter_t *e) + #if defined HAS_ARDUINOJSON @@ -233,19 +301,26 @@ void YAMLParser::handle_emitter_error(yaml_emitter_t *e) char* end; scalar = (char *)yamlNode->data.scalar.value; number = strtod(scalar, &end); + bool is_bool = false; + bool bool_value = false; bool is_string = (end == scalar || *end); + if( is_string && yaml_node_is_bool( yamlNode, &bool_value ) ) { + is_bool = true; + } switch( nt ) { case YAMLParser::SEQ_KEY: { JsonArray array = jsonNode[nodename]; - if(is_string) array.add( scalar ); - else array.add( number ); + if(is_bool) array.add( bool_value ); + else if(is_string) array.add( scalar ); + else array.add( number ); //YAML_LOG_d("[SEQ][%s][%d] => %s(%s)", nodename, array.size()-1, is_string?"string":"number", is_string?scalar:String(number).c_str() ); } break; case YAMLParser::MAP_KEY: - if(is_string) jsonNode[nodename] = scalar; - else jsonNode[nodename] = number; + if(is_bool) jsonNode[nodename] = bool_value; + else if(is_string) jsonNode[nodename] = scalar; + else jsonNode[nodename] = number; //YAML_LOG_d("[MAP][%d][%s] => %s(%s)", jsonNode.size()-1, nodename, is_string?"string":"number", is_string?scalar:String(number).c_str() ); break; default: YAML_LOG_e("Error invalid nesting type"); break; @@ -299,6 +374,7 @@ void YAMLParser::handle_emitter_error(yaml_emitter_t *e) } } + // JsonVariant deconstructor => YAML stream size_t serializeYml_JsonVariant( JsonVariant root, Stream &out, int depth, YAMLParser::JNestingType_t nt ) { @@ -346,20 +422,19 @@ void YAMLParser::handle_emitter_error(yaml_emitter_t *e) return serializeYml_JsonVariant( src_obj, dest_stream, 0, YAMLParser::NONE ); } - #if defined USE_STREAM_TO_STREAM + #if defined USE_STREAM_TO_STREAM && !defined HAS_CJSON size_t serializeYml( Stream &json_src_stream, Stream &yaml_dest_stream ) { - JsonObject src_obj; - if ( deserializeYml( src_obj, json_src_stream ) != DeserializationError::Ok ) { - YAML_LOG_e("unable to deserialize to temporary JsonObject, aborting"); - return 0; - } - return serializeYml_JsonVariant( src_obj, yaml_dest_stream, 0, YAMLParser::NONE ); + YAMLToArduinoJson *parser = new YAMLToArduinoJson(); + JsonObject tmpObj = parser->toJson( json_src_stream ); // decode yaml stream/string + auto ret = serializeYml_JsonVariant( tmpObj[ROOT_NODE], yaml_dest_stream, 0, YAMLParser::NONE ); + delete parser; + return ret; } #endif - DeserializationError deserializeYml( JsonDocument &dest_doc, Stream &src) + DeserializationError deserializeYml( JsonDocument &dest_doc, Stream &src ) { YAMLToArduinoJson *parser = new YAMLToArduinoJson(); JsonObject tmpObj = parser->toJson( src ); // decode yaml stream/string @@ -369,7 +444,7 @@ void YAMLParser::handle_emitter_error(yaml_emitter_t *e) } - DeserializationError deserializeYml( JsonDocument &dest_doc, const char *src) + DeserializationError deserializeYml( JsonDocument &dest_doc, const char *src ) { YAMLToArduinoJson *parser = new YAMLToArduinoJson(); JsonObject tmpObj = parser->toJson( src ); // decode yaml stream/string @@ -379,13 +454,13 @@ void YAMLParser::handle_emitter_error(yaml_emitter_t *e) } -#endif // __has_include() +#endif // HAS_ARDUINOJSON -#if __has_include() +#if defined HAS_CJSON // yaml_node_t deconstructor => cJSON Object @@ -406,7 +481,14 @@ void YAMLParser::handle_emitter_error(yaml_emitter_t *e) char * end; scalar = (char *)yamlNode->data.scalar.value; number = strtod(scalar, &end); - object = (end == scalar || *end) ? cJSON_CreateString(scalar) : cJSON_CreateNumber(number); + if( (end == scalar || *end) ) { // string or bool + bool bool_value; + if( yaml_node_is_bool( yamlNode, &bool_value ) ) { + object = cJSON_CreateBool( bool_value ); + } else { + object = cJSON_CreateString( scalar ); + } + } else object = cJSON_CreateNumber(number); } break; case YAML_SEQUENCE_NODE: @@ -467,11 +549,18 @@ void YAMLParser::handle_emitter_error(yaml_emitter_t *e) current_item = current_item->next; } } else { + // TODO: figure out how to get cJSON value without quotes char *value = cJSON_PrintUnformatted( root ); if( !value ) { YAML_LOG_e("node has no value!"); return 0; } + size_t value_len = strlen(value); + if( ( value_len > 0 && value[0]=='"' && value[value_len-1]=='"' ) || ( value_len > 0 && value[0]=='\'' && value[value_len-1]=='\'' ) ) { + // remove quotes from string + memmove( value, value+1, value_len-1 ); + value[value_len-2] = '\0'; + } switch(nt) { case YAMLParser::SEQ_KEY: out_size += out.printf("\n%s%s%s", indent(depth), "- ", value ); @@ -503,4 +592,22 @@ void YAMLParser::handle_emitter_error(yaml_emitter_t *e) } -#endif // __has_include() + #if defined USE_STREAM_TO_STREAM + size_t serializeYml( Stream &json_src_stream, Stream &yaml_dest_stream ) + { + cJSON* objPtr = cJSON_Parse("{}"); // quick allocation of empty object + if ( !deserializeYml( objPtr, json_src_stream ) ) { + YAML_LOG_e("unable to deserialize to temporary cJSONObject, aborting"); + cJSON_Delete( objPtr ); + return 0; + } + size_t bytes_written = serializeYml_cJSONObject( objPtr, yaml_dest_stream, 0, YAMLParser::NONE ); + cJSON_Delete( objPtr ); + return bytes_written; + } + #endif + + + + +#endif // HAS_CJSON diff --git a/src/ArduinoYaml.hpp b/src/ArduinoYaml.hpp index 9de37e1..cb402f9 100644 --- a/src/ArduinoYaml.hpp +++ b/src/ArduinoYaml.hpp @@ -36,13 +36,24 @@ extern "C" { #include "libyaml/yaml.h" // https://github.com/yaml/libyaml } + +#if defined ESP32 + #define USE_STREAM_TO_STREAM + #define HAS_CJSON + #define HAS_ARDUINOJSON +#endif + + #if defined ARDUINO_ARCH_SAMD || defined ARDUINO_ARCH_RP2040 || defined ESP8266 // __has_include() macro only sees inside the sketch folder, so assume ArduinoJson as a default dependancy #include #include #include - // also disable the "unstable" stream-to-stream function #define HAS_ARDUINOJSON + // also disable the "unstable" stream-to-stream function for low memory platforms + #if !defined ARDUINO_ARCH_SAMD + #define USE_STREAM_TO_STREAM + #endif #endif #if !defined HAS_ARDUINOJSON && __has_include() @@ -50,12 +61,13 @@ extern "C" { #define HAS_ARDUINOJSON #endif -#if defined ESP32 - // platform specific feature is unstable but recoverable with ESP32 devices family - #define USE_STREAM_TO_STREAM +#if !defined HAS_CJSON && __has_include() + // esp32 __has_include() macro works outside the sketch folder, so it's possible to guess + #define HAS_CJSON #endif + // provide a default String::Stream reader/writer for internals class StringStream : public Stream { @@ -85,12 +97,13 @@ class YAMLParser size_t bytesWritten() { return _bytes_written; } size_t bytesRead() { return _bytes_read; } String getYamlString() { return _yaml_string; } + void setYamlStream( Stream* stream ) { _yaml_stream = stream; } enum JNestingType_t { NONE, SEQ_KEY, MAP_KEY }; private: size_t _bytes_read; size_t _bytes_written; String _yaml_string; - StringStream _yaml_stream = StringStream(_yaml_string); + Stream *_yaml_stream = new StringStream(_yaml_string); void loadDocument(); void handle_parser_error(yaml_parser_t *parser); void handle_emitter_error(yaml_emitter_t* emitter); @@ -102,6 +115,11 @@ class YAMLParser +#if defined USE_STREAM_TO_STREAM + // JSON stream to JsonObject to YAML stream + size_t serializeYml( Stream &json_src_stream, Stream &yml_dest_stream ); +#endif + #if defined HAS_ARDUINOJSON @@ -155,10 +173,6 @@ class YAMLParser size_t serializeYml( JsonVariant src_obj, String &dest_string ); // ArduinoJSON object to YAML stream size_t serializeYml( JsonVariant src_obj, Stream &dest_stream ); - #if defined USE_STREAM_TO_STREAM - // JSON stream to JsonObject to YAML stream - size_t serializeYml( Stream &json_src_stream, Stream &yml_dest_stream ); - #endif // Deserialize YAML string to ArduinoJSON document DeserializationError deserializeYml( JsonDocument &dest_doc, Stream &src); @@ -174,6 +188,7 @@ class YAMLParser { YAMLToArduinoJson *parser = new YAMLToArduinoJson(); dest_obj = parser->toJson( src ); // decode yaml stream/string + dest_obj = dest_obj[ROOT_NODE]; size_t capacity = parser->bytesWritten()*2; delete parser; if( capacity == 0 ) { @@ -186,11 +201,11 @@ class YAMLParser } -#endif +#endif // HAS_ARDUINOJSON -#if __has_include() +#if defined HAS_CJSON // cJSON friendly functions and derivated class @@ -207,7 +222,7 @@ class YAMLParser YAMLToCJson() {}; ~YAMLToCJson() { if(_root) cJSON_Delete(_root); }; //void toJson(); - cJSON *toJson( yaml_document_t * document ){ + cJSON *toJson( yaml_document_t * document ) { yaml_node_t * node; if (node = yaml_document_get_root_node(document), !node) { YAML_LOG_w("No document defined."); return NULL; } return deserializeYml_cJSONObject(document, node); @@ -240,20 +255,5 @@ class YAMLParser } -#endif - -#if defined ARDUINO_ARCH_SAMD - // using slow copy instead of a macro, because std::string is incomplete with samd core - static String _indent_str; - static const char* indent( size_t size ) - { - _indent_str = ""; - for( size_t i=0;i