3030
3131#include " ArduinoYaml.hpp"
3232
33+
34+ String _indent_str;
35+ const char * indent ( size_t size )
36+ {
37+ _indent_str = " " ;
38+ for ( size_t i=0 ;i<size;i++ ) _indent_str += " " ;
39+ return _indent_str.c_str ();
40+ }
41+
42+
43+
44+ // YAML is very inclusive with booleans :-)
45+ // https://yaml.org/type/bool.html
46+
47+
48+ bool string_has_truthy_value ( String &_scalar )
49+ {
50+ return _scalar == " y" || _scalar == " Y" || _scalar == " yes" || _scalar == " Yes" || _scalar == " YES"
51+ || _scalar == " true" || _scalar == " True" || _scalar == " TRUE"
52+ || _scalar == " on" || _scalar == " On" || _scalar == " ON" ;
53+ }
54+
55+
56+ bool string_has_falsy_value ( String &_scalar )
57+ {
58+ return _scalar == " n" || _scalar == " N" || _scalar == " no" || _scalar == " No" || _scalar == " NO"
59+ || _scalar == " false" || _scalar == " False" || _scalar == " FALSE"
60+ || _scalar == " off" || _scalar == " Off" || _scalar == " OFF" ;
61+ }
62+
63+
64+ bool string_has_bool_value ( String &_scalar, bool *value_out )
65+ {
66+ bool has_truthy = string_has_truthy_value (_scalar);
67+ bool has_falsy = string_has_falsy_value (_scalar);
68+ if ( has_truthy || has_falsy ) {
69+ *value_out = has_truthy;
70+ return true ;
71+ }
72+ return false ;
73+ }
74+
75+
76+ bool yaml_node_is_bool ( yaml_node_t * yamlNode, bool *value_out )
77+ {
78+ switch (yamlNode->data .scalar .style ) {
79+ case YAML_PLAIN_SCALAR_STYLE:
80+ {
81+ String _scalar = String ( (char *)yamlNode->data .scalar .value );
82+ if ( string_has_bool_value (_scalar, value_out) ) {
83+ return true ;
84+ }
85+ }
86+ break ;
87+ case YAML_SINGLE_QUOTED_SCALAR_STYLE: /* YAML_LOG_e(" '")*/ ; break ;
88+ case YAML_DOUBLE_QUOTED_SCALAR_STYLE: /* YAML_LOG_e(" \"")*/ ; break ;
89+ case YAML_LITERAL_SCALAR_STYLE: /* YAML_LOG_e(" |")*/ ; break ;
90+ case YAML_FOLDED_SCALAR_STYLE: /* YAML_LOG_e(" >")*/ ; break ;
91+ case YAML_ANY_SCALAR_STYLE: /* abort(); */ break ;
92+ }
93+ return false ;
94+
95+ }
96+
97+
3398// used for internals, mainly because yaml_emitter_dump() destroys data after emitting
3499int yaml_copy_document (yaml_document_t *dest, yaml_document_t *src)
35100{
@@ -109,9 +174,7 @@ YAMLParser::~YAMLParser()
109174{
110175 yaml_parser_delete (&_parser);
111176 yaml_emitter_delete (&_emitter);
112- #if !defined ESP8266 // TODO: fix mem leak with esp8266
113- yaml_document_delete (&_document);
114- #endif
177+ yaml_document_delete (&_document);
115178}
116179
117180
@@ -163,7 +226,9 @@ void YAMLParser::loadDocument()
163226{
164227 yaml_document_t document;
165228 assert (yaml_emitter_initialize (&_emitter));
166- yaml_stream_handler_data_t shd = { &_yaml_stream, &_bytes_written };
229+ yaml_stream_handler_data_t shd = { _yaml_stream, &_bytes_written };
230+ yaml_emitter_set_canonical (&_emitter, 1 );
231+ yaml_emitter_set_unicode (&_emitter, 1 );
167232 yaml_emitter_set_output (&_emitter, &_yaml_stream_writer, &shd);
168233 yaml_emitter_open (&_emitter);
169234 if (!yaml_parser_load (&_parser, &document)) {
@@ -174,8 +239,10 @@ void YAMLParser::loadDocument()
174239 assert ( yaml_copy_document (&_document, &document) ); // copy into local document for later parsing
175240 assert ( yaml_emitter_dump (&_emitter, &document) ); // dump to emitter for output length evaluation
176241 _emitter_delete:
242+ yaml_parser_delete (&_parser);
177243 yaml_emitter_close (&_emitter);
178244 yaml_emitter_delete (&_emitter);
245+ yaml_document_delete (&document);
179246}
180247
181248
@@ -219,6 +286,7 @@ void YAMLParser::handle_emitter_error(yaml_emitter_t *e)
219286
220287
221288
289+
222290#if defined HAS_ARDUINOJSON
223291
224292
@@ -233,19 +301,26 @@ void YAMLParser::handle_emitter_error(yaml_emitter_t *e)
233301 char * end;
234302 scalar = (char *)yamlNode->data .scalar .value ;
235303 number = strtod (scalar, &end);
304+ bool is_bool = false ;
305+ bool bool_value = false ;
236306 bool is_string = (end == scalar || *end);
307+ if ( is_string && yaml_node_is_bool ( yamlNode, &bool_value ) ) {
308+ is_bool = true ;
309+ }
237310 switch ( nt ) {
238311 case YAMLParser::SEQ_KEY:
239312 {
240313 JsonArray array = jsonNode[nodename];
241- if (is_string) array.add ( scalar );
242- else array.add ( number );
314+ if (is_bool) array.add ( bool_value );
315+ else if (is_string) array.add ( scalar );
316+ else array.add ( number );
243317 // YAML_LOG_d("[SEQ][%s][%d] => %s(%s)", nodename, array.size()-1, is_string?"string":"number", is_string?scalar:String(number).c_str() );
244318 }
245319 break ;
246320 case YAMLParser::MAP_KEY:
247- if (is_string) jsonNode[nodename] = scalar;
248- else jsonNode[nodename] = number;
321+ if (is_bool) jsonNode[nodename] = bool_value;
322+ else if (is_string) jsonNode[nodename] = scalar;
323+ else jsonNode[nodename] = number;
249324 // YAML_LOG_d("[MAP][%d][%s] => %s(%s)", jsonNode.size()-1, nodename, is_string?"string":"number", is_string?scalar:String(number).c_str() );
250325 break ;
251326 default : YAML_LOG_e (" Error invalid nesting type" ); break ;
@@ -299,6 +374,7 @@ void YAMLParser::handle_emitter_error(yaml_emitter_t *e)
299374 }
300375 }
301376
377+
302378 // JsonVariant deconstructor => YAML stream
303379 size_t serializeYml_JsonVariant ( JsonVariant root, Stream &out, int depth, YAMLParser::JNestingType_t nt )
304380 {
@@ -346,20 +422,19 @@ void YAMLParser::handle_emitter_error(yaml_emitter_t *e)
346422 return serializeYml_JsonVariant ( src_obj, dest_stream, 0 , YAMLParser::NONE );
347423 }
348424
349- #if defined USE_STREAM_TO_STREAM
425+ #if defined USE_STREAM_TO_STREAM && !defined HAS_CJSON
350426 size_t serializeYml ( Stream &json_src_stream, Stream &yaml_dest_stream )
351427 {
352- JsonObject src_obj;
353- if ( deserializeYml ( src_obj, json_src_stream ) != DeserializationError::Ok ) {
354- YAML_LOG_e (" unable to deserialize to temporary JsonObject, aborting" );
355- return 0 ;
356- }
357- return serializeYml_JsonVariant ( src_obj, yaml_dest_stream, 0 , YAMLParser::NONE );
428+ YAMLToArduinoJson *parser = new YAMLToArduinoJson ();
429+ JsonObject tmpObj = parser->toJson ( json_src_stream ); // decode yaml stream/string
430+ auto ret = serializeYml_JsonVariant ( tmpObj[ROOT_NODE], yaml_dest_stream, 0 , YAMLParser::NONE );
431+ delete parser;
432+ return ret;
358433 }
359434 #endif
360435
361436
362- DeserializationError deserializeYml ( JsonDocument &dest_doc, Stream &src)
437+ DeserializationError deserializeYml ( JsonDocument &dest_doc, Stream &src )
363438 {
364439 YAMLToArduinoJson *parser = new YAMLToArduinoJson ();
365440 JsonObject tmpObj = parser->toJson ( src ); // decode yaml stream/string
@@ -369,7 +444,7 @@ void YAMLParser::handle_emitter_error(yaml_emitter_t *e)
369444 }
370445
371446
372- DeserializationError deserializeYml ( JsonDocument &dest_doc, const char *src)
447+ DeserializationError deserializeYml ( JsonDocument &dest_doc, const char *src )
373448 {
374449 YAMLToArduinoJson *parser = new YAMLToArduinoJson ();
375450 JsonObject tmpObj = parser->toJson ( src ); // decode yaml stream/string
@@ -379,13 +454,13 @@ void YAMLParser::handle_emitter_error(yaml_emitter_t *e)
379454 }
380455
381456
382- #endif // __has_include(<ArduinoJson.h>)
457+ #endif // HAS_ARDUINOJSON
383458
384459
385460
386461
387462
388- #if __has_include(<cJSON.h>)
463+ #if defined HAS_CJSON
389464
390465
391466 // yaml_node_t deconstructor => cJSON Object
@@ -406,7 +481,14 @@ void YAMLParser::handle_emitter_error(yaml_emitter_t *e)
406481 char * end;
407482 scalar = (char *)yamlNode->data .scalar .value ;
408483 number = strtod (scalar, &end);
409- object = (end == scalar || *end) ? cJSON_CreateString (scalar) : cJSON_CreateNumber (number);
484+ if ( (end == scalar || *end) ) { // string or bool
485+ bool bool_value;
486+ if ( yaml_node_is_bool ( yamlNode, &bool_value ) ) {
487+ object = cJSON_CreateBool ( bool_value );
488+ } else {
489+ object = cJSON_CreateString ( scalar );
490+ }
491+ } else object = cJSON_CreateNumber (number);
410492 }
411493 break ;
412494 case YAML_SEQUENCE_NODE:
@@ -467,11 +549,18 @@ void YAMLParser::handle_emitter_error(yaml_emitter_t *e)
467549 current_item = current_item->next ;
468550 }
469551 } else {
552+ // TODO: figure out how to get cJSON value without quotes
470553 char *value = cJSON_PrintUnformatted ( root );
471554 if ( !value ) {
472555 YAML_LOG_e (" node has no value!" );
473556 return 0 ;
474557 }
558+ size_t value_len = strlen (value);
559+ if ( ( value_len > 0 && value[0 ]==' "' && value[value_len-1 ]==' "' ) || ( value_len > 0 && value[0 ]==' \' ' && value[value_len-1 ]==' \' ' ) ) {
560+ // remove quotes from string
561+ memmove ( value, value+1 , value_len-1 );
562+ value[value_len-2 ] = ' \0 ' ;
563+ }
475564 switch (nt) {
476565 case YAMLParser::SEQ_KEY:
477566 out_size += out.printf (" \n %s%s%s" , indent (depth), " - " , value );
@@ -503,4 +592,22 @@ void YAMLParser::handle_emitter_error(yaml_emitter_t *e)
503592 }
504593
505594
506- #endif // __has_include(<cJSON.h>)
595+ #if defined USE_STREAM_TO_STREAM
596+ size_t serializeYml ( Stream &json_src_stream, Stream &yaml_dest_stream )
597+ {
598+ cJSON* objPtr = cJSON_Parse (" {}" ); // quick allocation of empty object
599+ if ( !deserializeYml ( objPtr, json_src_stream ) ) {
600+ YAML_LOG_e (" unable to deserialize to temporary cJSONObject, aborting" );
601+ cJSON_Delete ( objPtr );
602+ return 0 ;
603+ }
604+ size_t bytes_written = serializeYml_cJSONObject ( objPtr, yaml_dest_stream, 0 , YAMLParser::NONE );
605+ cJSON_Delete ( objPtr );
606+ return bytes_written;
607+ }
608+ #endif
609+
610+
611+
612+
613+ #endif // HAS_CJSON
0 commit comments