diff --git a/src/ixml/cl_ixml.clas.locals_imp.abap b/src/ixml/cl_ixml.clas.locals_imp.abap
index 03d8ee0b..202e63af 100644
--- a/src/ixml/cl_ixml.clas.locals_imp.abap
+++ b/src/ixml/cl_ixml.clas.locals_imp.abap
@@ -1303,6 +1303,7 @@ CLASS lcl_parser IMPLEMENTATION.
DATA lv_xml TYPE string.
DATA lv_rest TYPE string.
DATA lv_whitespace TYPE string.
+ DATA lv_in_element TYPE abap_bool.
DATA lv_bom TYPE c LENGTH 1.
DATA lv_offset TYPE i.
DATA lv_value TYPE string.
@@ -1341,6 +1342,7 @@ CLASS lcl_parser IMPLEMENTATION.
FIND FIRST OCCURRENCE OF '?>' IN lv_xml MATCH OFFSET lv_offset.
ASSERT lv_offset > 0.
lv_offset = lv_offset + 2.
+ lv_in_element = abap_false.
ELSEIF lv_xml CP '<*'.
* start or close tag
FIND FIRST OCCURRENCE OF REGEX lc_regex_tag IN lv_xml RESULTS ls_match.
@@ -1354,6 +1356,7 @@ CLASS lcl_parser IMPLEMENTATION.
IF lv_xml CP '*'.
* todo: check its the right name
lo_parent ?= lo_parent->if_ixml_node~get_parent( ).
+ lv_in_element = abap_false.
ELSE.
CREATE OBJECT lo_node EXPORTING ii_parent = lo_parent.
IF lv_name CA ':'.
@@ -1364,6 +1367,9 @@ CLASS lcl_parser IMPLEMENTATION.
IF lv_tag NP '*/>'.
lo_parent = lo_node.
+ lv_in_element = abap_true.
+ ELSE.
+ lv_in_element = abap_false.
ENDIF.
ENDIF.
@@ -1384,6 +1390,7 @@ CLASS lcl_parser IMPLEMENTATION.
CREATE OBJECT lo_node EXPORTING ii_parent = lo_parent.
lo_node->if_ixml_node~set_name( '#text' ).
lo_node->if_ixml_node~set_value( lcl_escape=>unescape_value( lv_value ) ).
+ lv_in_element = abap_false.
ENDIF.
lv_xml = lv_xml+lv_offset.
@@ -1394,7 +1401,14 @@ CLASS lcl_parser IMPLEMENTATION.
lv_rest = lv_xml.
SHIFT lv_rest LEFT DELETING LEADING lv_whitespace.
IF lv_rest IS INITIAL OR lv_rest(1) = '<'.
- lv_xml = lv_rest.
+* whitespace that stands between a start tag and its own end tag is the
+* content of that element, ` ` carries a single blank, so it
+* is left for the value branch below - everything else is formatting
+ IF lv_in_element = abap_true AND lv_rest CP '*' AND lv_rest <> lv_xml.
+ lv_in_element = abap_false.
+ ELSE.
+ lv_xml = lv_rest.
+ ENDIF.
ENDIF.
ENDWHILE.
diff --git a/src/ixml/cl_ixml.clas.testclasses.abap b/src/ixml/cl_ixml.clas.testclasses.abap
index d95cb4a0..f971856e 100644
--- a/src/ixml/cl_ixml.clas.testclasses.abap
+++ b/src/ixml/cl_ixml.clas.testclasses.abap
@@ -17,6 +17,8 @@ CLASS ltcl_xml DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT FINAL.
METHODS parse_basic FOR TESTING RAISING cx_static_check.
METHODS root_element_after_crlf FOR TESTING RAISING cx_static_check.
METHODS first_child_after_crlf FOR TESTING RAISING cx_static_check.
+ METHODS parse_blank_only_value FOR TESTING RAISING cx_static_check.
+ METHODS parse_indented_children FOR TESTING RAISING cx_static_check.
METHODS parse_value_with_newline FOR TESTING RAISING cx_static_check.
METHODS parse_bom FOR TESTING RAISING cx_static_check.
METHODS parse_empty FOR TESTING RAISING cx_static_check.
@@ -474,6 +476,35 @@ CLASS ltcl_xml IMPLEMENTATION.
ENDMETHOD.
+ METHOD parse_blank_only_value.
+
+ DATA li_root TYPE REF TO if_ixml_element.
+
+ li_root = parse( |-
| )->get_root_element( ).
+
+ cl_abap_unit_assert=>assert_equals(
+ act = li_root->get_first_child( )->get_value( )
+ exp = ` ` ).
+
+ ENDMETHOD.
+
+ METHOD parse_indented_children.
+
+ DATA li_root TYPE REF TO if_ixml_element.
+ DATA li_item TYPE REF TO if_ixml_node.
+
+ " whitespace between two tags stays formatting, the first child of root
+ " is the element and not a #text node
+ li_root = parse( |\n - A
\n| )->get_root_element( ).
+
+ li_item = li_root->get_first_child( ).
+
+ cl_abap_unit_assert=>assert_equals(
+ act = li_item->get_name( )
+ exp = `item` ).
+
+ ENDMETHOD.
+
METHOD parse_bom.
DATA lv_bom TYPE c LENGTH 1.
diff --git a/src/kernel/call_transformation/kernel_call_transformation.clas.testclasses.abap b/src/kernel/call_transformation/kernel_call_transformation.clas.testclasses.abap
index e6f9b0e5..bd697983 100644
--- a/src/kernel/call_transformation/kernel_call_transformation.clas.testclasses.abap
+++ b/src/kernel/call_transformation/kernel_call_transformation.clas.testclasses.abap
@@ -96,6 +96,8 @@ CLASS ltcl_call_transformation DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATI
METHODS escape_char_data FOR TESTING RAISING cx_static_check.
METHODS escape_char_data_roundtrip FOR TESTING RAISING cx_static_check.
METHODS escape_entity_text_roundtrip FOR TESTING RAISING cx_static_check.
+ METHODS blank_only_roundtrip FOR TESTING RAISING cx_static_check.
+ METHODS newline_only_roundtrip FOR TESTING RAISING cx_static_check.
METHODS newline_roundtrip FOR TESTING RAISING cx_static_check.
METHODS to_string_simple FOR TESTING RAISING cx_static_check.
METHODS to_string_empty FOR TESTING RAISING cx_static_check.
@@ -363,6 +365,55 @@ CLASS ltcl_call_transformation IMPLEMENTATION.
exp = lv_exp ).
ENDMETHOD.
+ METHOD blank_only_roundtrip.
+* a value of one blank is content, not formatting
+ DATA lv_xml TYPE string.
+ DATA: BEGIN OF ls_data,
+ field TYPE string,
+ END OF ls_data.
+
+ ls_data-field = ` `.
+
+ CALL TRANSFORMATION id
+ SOURCE data = ls_data
+ RESULT XML lv_xml.
+
+ CLEAR ls_data.
+
+ CALL TRANSFORMATION id
+ SOURCE XML lv_xml
+ RESULT data = ls_data.
+
+ cl_abap_unit_assert=>assert_equals(
+ act = ls_data-field
+ exp = ` ` ).
+ ENDMETHOD.
+
+ METHOD newline_only_roundtrip.
+ DATA lv_xml TYPE string.
+ DATA lv_exp TYPE string.
+ DATA: BEGIN OF ls_data,
+ field TYPE string,
+ END OF ls_data.
+
+ lv_exp = cl_abap_char_utilities=>newline.
+ ls_data-field = lv_exp.
+
+ CALL TRANSFORMATION id
+ SOURCE data = ls_data
+ RESULT XML lv_xml.
+
+ CLEAR ls_data.
+
+ CALL TRANSFORMATION id
+ SOURCE XML lv_xml
+ RESULT data = ls_data.
+
+ cl_abap_unit_assert=>assert_equals(
+ act = ls_data-field
+ exp = lv_exp ).
+ ENDMETHOD.
+
METHOD convert_json_to_sxml.
DATA lo_writer TYPE REF TO cl_sxml_string_writer.
lo_writer = cl_sxml_string_writer=>create( ).