I'm not entirely sure if a similar solution exists; I've tried searching through blog posts in the ABAP Development space and couldn't find any match (although there are over 1000 results for "abap xml").
Basically, more out of boredom and willing to learn more about the capabilities of the iXML Library, I made an ABAP/4 class that can transform any data (simple data, structure, table type) at any depth into XML format and vice versa. Even though, bringing XML to ABAP/4 only matches elements with the same name, ignoring extra data.
For example:
Given the following nested-structured local transparent table:
DATA :
BEGIN OF ls_data.
INCLUDE STRUCTURE spfli.
DATA :
scarr TYPE scarr,
sflight TYPE sflight_tab1,
END OF ls_data,
lt_data LIKE TABLE OF ls_data.
Filled with test data:
SELECT *
UP TO 10 ROWS
FROM spfli
INTO CORRESPONDING FIELDS OF TABLE lt_data.
FIELD-SYMBOLS : <fs_data> LIKE LINE OF lt_data.
LOOP AT lt_data ASSIGNING <fs_data>.
SELECT SINGLE *
FROM scarr
INTO CORRESPONDING FIELDS OF <fs_data>-scarr
WHERE carrid = <fs_data>-carrid.
SELECT *
FROM sflight
INTO CORRESPONDING FIELDS OF TABLE <fs_data>-sflight
WHERE carrid = <fs_data>-carrid
AND connid = <fs_data>-connid.
ENDLOOP.
The result is:
The class looks as follows (I will also attach it as text at the bottom together with the demo report):
class ZCL_XML_UTIL definition
public
create public .
public section.
*"* public components of class ZCL_XML_UTIL
*"* do not include other source files here!!!
methods CONSTRUCTOR .
methods ABAP_TO_XML
importing
!IM_DATA type ANY
exporting
!EX_CONTENT type XSTRING
raising
ZCX_TYPE_NOT_SUPPORTED .
methods XML_TO_ABAP
importing
!IM_CONTENT type XSTRING
exporting
!EX_DATA type ANY
raising
ZCX_TYPE_NOT_SUPPORTED .
protected section.
*"* protected components of class ZCL_XML_UTIL
*"* do not include other source files here!!!
data MO_IXML type ref to IF_IXML .
data MO_DOCUMENT type ref to IF_IXML_DOCUMENT .
private section.
*"* private components of class ZCL_XML_UTIL
*"* do not include other source files here!!!
methods PROCESS
importing
!IM_NODE type ref to IF_IXML_NODE
!IM_NAME type STRING
!IM_DATA type ANY
raising
ZCX_TYPE_NOT_SUPPORTED .
methods CREATE_ELEMENT
importing
!IM_NAME type STRING
!IM_VALUE type STRING optional
returning
value(RE_ELEMENT) type ref to IF_IXML_ELEMENT .
methods TRAVERSE
importing
!IM_CURRENT_NODE type ref to IF_IXML_NODE
changing
!CH_DATA type ANY
raising
ZCX_TYPE_NOT_SUPPORTED .
class-methods AS_STRING
importing
!IM_DATA type ANY
returning
value(RE_DATA_STRING) type STRING .
ENDCLASS.
CLASS ZCL_XML_UTIL IMPLEMENTATION.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_XML_UTIL->ABAP_TO_XML
* +-------------------------------------------------------------------------------------------------+
* | [--->] IM_DATA TYPE ANY
* | [<---] EX_CONTENT TYPE XSTRING
* | [!CX!] ZCX_TYPE_NOT_SUPPORTED
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD abap_to_xml.
CLEAR : ex_content.
* create .xml document
mo_document = mo_ixml->create_document( ).
* set encoding to UTF-8 (Unicode Transformation Format)
* 8-bit variable-width encoding maximizes compatibility with ASCII
mo_document->set_encoding( mo_ixml->create_encoding(
byte_order = 0
character_set = 'UTF-8' ) ).
IF NOT im_data IS INITIAL.
* use mo_document as root
process( im_node = mo_document
im_name = 'data'
im_data = im_data ).
ENDIF.
* render .xml document with output stream
mo_document->render(
ostream = mo_ixml->create_stream_factory( )->create_ostream_xstring(
string = ex_content ) ).
FREE : mo_document.
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Private Method ZCL_XML_UTIL=>AS_STRING
* +-------------------------------------------------------------------------------------------------+
* | [--->] IM_DATA TYPE ANY
* | [<-()] RE_DATA_STRING TYPE STRING
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD as_string.
MOVE im_data TO re_data_string.
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_XML_UTIL->CONSTRUCTOR
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD CONSTRUCTOR.
* get iXML library instance
mo_ixml = cl_ixml=>create( ).
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZCL_XML_UTIL->CREATE_ELEMENT
* +-------------------------------------------------------------------------------------------------+
* | [--->] IM_NAME TYPE STRING
* | [--->] IM_VALUE TYPE STRING(optional)
* | [<-()] RE_ELEMENT TYPE REF TO IF_IXML_ELEMENT
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD CREATE_ELEMENT.
DATA :
lv_name TYPE string VALUE IS INITIAL.
* element names look cooler in lower case :smile:
lv_name = to_lower( im_name ).
* create element with given name
re_element = mo_document->create_element( name = lv_name ).
* if element is leaf, set corresponding value
IF im_value IS SUPPLIED.
re_element->set_value( im_value ).
ENDIF.
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZCL_XML_UTIL->PROCESS
* +-------------------------------------------------------------------------------------------------+
* | [--->] IM_NODE TYPE REF TO IF_IXML_NODE
* | [--->] IM_NAME TYPE STRING
* | [--->] IM_DATA TYPE ANY
* | [!CX!] ZCX_TYPE_NOT_SUPPORTED
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD process.
DATA :
lo_type TYPE REF TO cl_abap_typedescr,
lo_struct TYPE REF TO cl_abap_structdescr,
lo_table TYPE REF TO cl_abap_tabledescr,
lo_data TYPE REF TO data,
lo_element TYPE REF TO if_ixml_element,
lv_name TYPE string VALUE IS INITIAL.
FIELD-SYMBOLS :
<ft_data> TYPE STANDARD TABLE,
<fs_component> TYPE abap_compdescr,
<fs_data> TYPE any,
<fv_value> TYPE any.
* determine ABAP/4 data type, support only:
* - simple type
* - structure
* - table type
lo_type = cl_abap_typedescr=>describe_by_data( im_data ).
CASE lo_type->kind.
WHEN cl_abap_typedescr=>kind_elem.
* create element
lo_element = create_element(
im_name = im_name
im_value = as_string( im_data ) ).
WHEN cl_abap_typedescr=>kind_struct.
* create parent element
lo_element = create_element( im_name = im_name ).
* process each structure component independently
lo_struct ?= cl_abap_structdescr=>describe_by_data( im_data ).
LOOP AT lo_struct->components ASSIGNING <fs_component>.
ASSIGN COMPONENT <fs_component>-name OF STRUCTURE im_data TO <fv_value>.
CHECK sy-subrc IS INITIAL.
lv_name = <fs_component>-name.
process( im_node = lo_element
im_name = lv_name
im_data = <fv_value> ).
ENDLOOP.
WHEN cl_abap_typedescr=>kind_table.
lo_table ?= cl_abap_tabledescr=>describe_by_data( im_data ).
CREATE DATA lo_data TYPE HANDLE lo_table.
ASSIGN lo_data->* TO <ft_data>.
<ft_data> = im_data.
* create parent element
lo_element = create_element( im_name = im_name ).
* process each table line independently
LOOP AT <ft_data> ASSIGNING <fs_data>.
process( im_node = lo_element
im_name = im_name
im_data = <fs_data> ).
ENDLOOP.
WHEN OTHERS.
TRY.
RAISE EXCEPTION TYPE zcx_type_not_supported.
ENDTRY.
ENDCASE.
IF lo_element IS BOUND.
im_node->append_child( lo_element ).
ENDIF.
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZCL_XML_UTIL->TRAVERSE
* +-------------------------------------------------------------------------------------------------+
* | [--->] IM_CURRENT_NODE TYPE REF TO IF_IXML_NODE
* | [<-->] CH_DATA TYPE ANY
* | [!CX!] ZCX_TYPE_NOT_SUPPORTED
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD traverse.
DATA :
lo_type TYPE REF TO cl_abap_typedescr,
lo_struct TYPE REF TO cl_abap_structdescr,
lo_table TYPE REF TO cl_abap_tabledescr,
lo_data TYPE REF TO data,
lo_iterator TYPE REF TO if_ixml_node_iterator,
lo_node_list TYPE REF TO if_ixml_node_list,
lo_node TYPE REF TO if_ixml_node,
lv_node_name TYPE string VALUE IS INITIAL,
lv_match TYPE boolean.
FIELD-SYMBOLS :
<ft_data> TYPE STANDARD TABLE,
<fs_component> TYPE abap_compdescr,
<fs_data> TYPE any,
<fv_value> TYPE any.
* determine ABAP/4 data type, support only:
* - simple type
* - structure
* - table type
lo_type = cl_abap_typedescr=>describe_by_data( ch_data ).
CASE lo_type->kind.
WHEN cl_abap_typedescr=>kind_elem.
ch_data = im_current_node->get_value( ).
WHEN cl_abap_typedescr=>kind_struct.
lo_node_list = im_current_node->get_children( ).
* process each structure component independently
lo_struct ?= cl_abap_structdescr=>describe_by_data( ch_data ).
LOOP AT lo_struct->components ASSIGNING <fs_component>.
ASSIGN COMPONENT <fs_component>-name OF STRUCTURE ch_data TO <fv_value>.
CHECK sy-subrc IS INITIAL.
* create new iterator with each step
lo_iterator = lo_node_list->create_iterator( ).
lo_node = lo_iterator->get_next( ).
WHILE lo_node IS BOUND
OR lv_match EQ abap_true.
* xml element names can contain lower case characters
lv_node_name = to_upper( lo_node->get_name( ) ).
IF lv_node_name EQ <fs_component>-name.
lv_match = abap_true.
EXIT.
ENDIF.
lo_node = lo_iterator->get_next( ).
ENDWHILE.
CHECK lv_match EQ abap_true.
traverse( EXPORTING im_current_node = lo_node
CHANGING ch_data = <fv_value> ).
CLEAR : lv_match.
ENDLOOP.
WHEN cl_abap_typedescr=>kind_table.
lo_table ?= cl_abap_tabledescr=>describe_by_data( ch_data ).
CREATE DATA lo_data TYPE HANDLE lo_table.
ASSIGN lo_data->* TO <ft_data>.
lo_node_list = im_current_node->get_children( ).
lo_iterator = lo_node_list->create_iterator( ).
lo_node = lo_iterator->get_next( ).
WHILE lo_node IS BOUND.
APPEND INITIAL LINE TO <ft_data> ASSIGNING <fs_data>.
traverse( EXPORTING im_current_node = lo_node
CHANGING ch_data = <fs_data> ).
lo_node = lo_iterator->get_next( ).
ENDWHILE.
IF NOT <ft_data> IS INITIAL.
ch_data = <ft_data>.
ENDIF.
WHEN OTHERS.
TRY.
RAISE EXCEPTION TYPE zcx_type_not_supported.
ENDTRY.
ENDCASE.
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_XML_UTIL->XML_TO_ABAP
* +-------------------------------------------------------------------------------------------------+
* | [--->] IM_CONTENT TYPE XSTRING
* | [<---] EX_DATA TYPE ANY
* | [!CX!] ZCX_TYPE_NOT_SUPPORTED
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD xml_to_abap.
CLEAR : ex_data.
DATA :
lo_istream TYPE REF TO if_ixml_istream,
lo_stream_factory TYPE REF TO if_ixml_stream_factory,
lo_iterator TYPE REF TO if_ixml_node_iterator.
ASSERT NOT im_content IS INITIAL.
IF NOT ex_data IS REQUESTED.
RETURN.
ENDIF.
* create .xml document
mo_document = mo_ixml->create_document( ).
lo_stream_factory = mo_ixml->create_stream_factory( ).
lo_istream = lo_stream_factory->create_istream_xstring( im_content ).
* parse .xml document with given input stream
IF mo_ixml->create_parser(
document = mo_document
istream = lo_istream
stream_factory = lo_stream_factory )->parse( ) IS INITIAL.
* create iterator
lo_iterator = mo_document->create_iterator( 0 ).
* skip #document element
lo_iterator->get_next( ).
* start with root
traverse( EXPORTING im_current_node = lo_iterator->get_next( )
CHANGING ch_data = ex_data ).
ENDIF.
FREE : mo_document.
ENDMETHOD.
ENDCLASS.
Feedback is most welcomed!
Tudor
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
1 | |
1 | |
1 | |
1 | |
1 | |
1 | |
1 | |
1 |