Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion src/ixml/cl_ixml.clas.locals_imp.abap
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand All @@ -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 ':'.
Expand All @@ -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.

Expand All @@ -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.
Expand All @@ -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, `<FIELD> </FIELD>` 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.

Expand Down
31 changes: 31 additions & 0 deletions src/ixml/cl_ixml.clas.testclasses.abap
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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( |<root><item> </item></root>| )->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( |<root>\n <item>A</item>\n</root>| )->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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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( ).
Expand Down