Automatic mapping from XML into complex internal data structure in abap.
In a recent project we need to realize a lot of xml interface over http. My team and I want to find a good solution to manage them.
Our mainly idea is to get a easy and automatic way to have internal data structure that reflect xml (and xsd) data.
Starting from this we start to analyze problem from a simple XML File:
<?xml version=”1.0″ encoding=”ISO-8859-1″?><shiporder><ordernumber>10</ordernumber><orderperson>John Smith</orderperson><shipto><name>Ola Nordmann</name><address>Langgt 23</address><city>4000 Stavanger</city><country>Norway</country></shipto><item><title>Empire Burlesque</title><note>Special Edition</note><quantity>1</quantity><price>10.90</price></item><item><title>Hide your heart</title><quantity>1</quantity><price>9.90</price></item></shiporder>
This is define this XSD (to keep it simple type=”xs:string” is used in all element …)
<?xml version=”1.0″ encoding=”ISO-8859-1″ ?><xs:schema xmlns:xs=”http://www.w3.org/2001/XMLSchema”><xs:element name=”shiporder”><xs:complexType><xs:sequence><xs:element name=”ordernumber” /><xs:element name=”orderperson” /></xs:sequence></xs:complexType><xs:element name=”shipto”><xs:complexType><xs:sequence><xs:element name=”name” /><xs:element name=”address” /><xs:element name=”city” /><xs:element name=”country” /></xs:sequence></xs:complexType></xs:element><xs:element name=”item” maxOccurs=”unbounded”><xs:complexType><xs:sequence><xs:element name=”title” /><xs:element name=”note” /><xs:element name=”quantity” /><xs:element name=”price” /></xs:sequence></xs:complexType></xs:element></xs:element></xs:schema>
For deeper node “item” we expect this kind of structure:
* <xs:element name=”item” maxOccurs=”unbounded”>* <xs:complexType>* <xs:sequence>* <xs:element name=”title” />* <xs:element name=”note” />* <xs:element name=”quantity” />* <xs:element name=”price” />* </xs:sequence>* </xs:complexType>* </xs:element>Types: begin of xml_item,xml_title type string,xml_note type string,xml_quantity type string,xml_price type string,end of xml_item.
For element “shipto” we expect a structure (element has no unbounded attribute). So the abap code is:
* <xs:element name=”shipto”>* <xs:complexType>* <xs:sequence>* <xs:element name=”name” />* <xs:element name=”address” />* <xs:element name=”city” />* <xs:element name=”country” />* </xs:sequence>* </xs:complexType>* </xs:element>types: begin of xml_shipto,xml_name type string,xml_address type string,xml_city type string,xml_country type string,end of xml_shipto.
Finally the root node is:
* <xs:element name=”shiporder”>* <xs:complexType>* <xs:sequence>* <xs:element name=”ordernumber” />* <xs:element name=”orderperson” />* </xs:sequence>* </xs:complexType>* <xs:element name=”shipto”>* …* </xs:element>* <xs:element name=”item” maxOccurs=”unbounded”>* ….* </xs:element>* </xs:element>data: begin of xml_shiporder,xml_ordernumber type string,xml_orderperson type string,xml_shipto type xml_shipto,xml_item TYPE STANDARD TABLE OF xml_item WITH DEFAULT KEY, ” item is unboundend.. so is a tableend of xml_shiporder.data: begin of xml_structure,xml_shiporder type xml_shiporder,end of xml_structure.
Note that item element is type table of because item element is unbounded. This means that we can have one or more item.
This structure reflect the xml: one record for shiporder, nested structure for shipto, nested table for item.
Now using dynamic data identification we parse xml and map data into internal table:
**** lv_xml is the xml string file…DATA: lo_ixml TYPE REF TO if_ixml,lo_factory TYPE REF TO if_ixml_stream_factory,lo_parser TYPE REF TO if_ixml_parser,lo_istream TYPE REF TO if_ixml_istream,lo_document TYPE REF TO if_ixml_document,lo_node TYPE REF TO if_ixml_node,lv_is_leaf.lo_ixml = cl_ixml=>create( ).lo_factory = lo_ixml->create_stream_factory( ).lo_istream = lo_factory->create_istream_string( string = lv_xml ).lo_document = lo_ixml->create_document( ).lo_parser = lo_ixml->create_parser( document = lo_documentstream_factory = lo_factoryistream = lo_istream ).CHECK: lo_parser->parse( ) IS INITIAL.DATA: lv_nodeinfo TYPE string.lo_node = lo_document->get_first_child( ). ” Get Root* lv_nodeinfo = lo_node->get_name( ).PERFORM parse_xml_and_fill_abap USING lo_node‘XML_SHIPORDER’ ” root name‘XML_’ ” Fields prefix <XML_>CHANGING xml_structure.
The core functionality is this simple recursive form routines:
FORM parse_xml_and_fill_abap USING value(xml_node) TYPE REF TO if_ixml_nodevalue(table_name)value(abap_field_prefix)CHANGING abap_data.* Strutture ABAPDATA: ld_abap_record TYPE REF TO data.FIELD-SYMBOLS <fs_abap_table> TYPE STANDARD TABLE.FIELD-SYMBOLS <fs_abap_record> TYPE ANY.DATA: lo_abap_type_descr TYPE REF TO cl_abap_typedescr.DATA: lv_abap_data_type.* Parsing XMLDATA: lo_child_list TYPE REF TO if_ixml_node_list.DATA: lv_child_num TYPE i.DATA: lo_child_node TYPE REF TO if_ixml_node.DATA: lv_node_idx TYPE i.DATA: lv_is_leaf_father.* Campo – Nome/ValoreDATA: lv_node_name TYPE string.DATA: lv_field_name TYPE string.DATA: lv_field_value TYPE string.FIELD-SYMBOLS <fs_abap_field> TYPE ANY.TRANSLATE table_name TO UPPER CASE.ASSIGN COMPONENT table_name OF STRUCTURE abap_data TO <fs_abap_record>.CHECK sy-subrc EQ 0.cl_abap_typedescr=>describe_by_data( EXPORTING p_data = <fs_abap_record>RECEIVING p_descr_ref = lo_abap_type_descr ).IF lo_abap_type_descr->type_kind = lo_abap_type_descr->typekind_table.ASSIGN COMPONENT table_name OF STRUCTURE abap_data TO <fs_abap_table>.CREATE DATA ld_abap_record LIKE LINE OF <fs_abap_table>.ASSIGN ld_abap_record->* TO <fs_abap_record>.ENDIF.lo_child_list = xml_node->get_children( ).lv_child_num = xml_node->num_children( ).DO lv_child_num TIMES.xml_node = lo_child_list->get_item( lv_node_idx ).lv_node_name = xml_node->get_name( ).CONCATENATE abap_field_prefix lv_node_name INTO lv_field_name.TRANSLATE lv_field_name TO UPPER CASE.lo_child_node = xml_node->get_first_child( ).IF lo_child_node IS NOT INITIAL.lv_is_leaf_father = lo_child_node->is_leaf( ).IF lv_is_leaf_father IS INITIAL.PERFORM parse_xml_and_fill_abap USING xml_nodelv_field_nameabap_field_prefixCHANGING <fs_abap_record>.ELSE.lv_field_value = xml_node->get_value( ).ASSIGN COMPONENT lv_field_name OF STRUCTURE <fs_abap_record> TO <fs_abap_field>.IF sy-subrc IS INITIAL.<fs_abap_field> = lv_field_value.ENDIF.ENDIF.ENDIF.lv_node_idx = lv_node_idx + 1.ENDDO.IF lo_abap_type_descr->type_kind = lo_abap_type_descr->typekind_table.APPEND <fs_abap_record> TO <fs_abap_table>.ENDIF.ENDFORM. ”PARSE_XML_AND_FILL_TABLE
If you want to see a complex example you can take a look to this xml
<?xml version=”1.0″ encoding=”utf-8″?><submitBasketRequest><products><product><code>51863899</code><description>SEMIALBERO</description><quantity>1</quantity><type>NORMAL</type><prod_char><color>RED</color><size>SMALL</size></prod_char><prod_char><color>BLU</color><size>MEDIUM</size></prod_char></product><product><code>55214685</code><description>ALBERO</description><quantity>2</quantity><type>NORMAL</type><prod_char><color>WHITE</color><size>BIG</size></prod_char></product><product><code>99214685</code><description>QUARTOALBERO</description><quantity>3</quantity><type>SPECIAL</type><prod_char><color>GREEN</color><size>XXS</size></prod_char><prod_char><color>BLU</color><size>MEDIUM</size></prod_char><prod_char><color>RED</color><size>SMALL</size></prod_char><prod_char><color>BLACK</color><size>XXL</size></prod_char></product><product><code>99863899</code><description>OTTAVOALBERO</description><quantity>7</quantity><type>SPECIAL</type></product><product><code>88888888</code><description>SEDICESIMOALBERO</description><quantity>9</quantity><type>NORMAL</type><prod_char><color>RED</color><size>SMALL</size></prod_char></product><dummy_serv><service><code>SERVICE-01</code><description>MAH</description><serv_char><day>MONDAY</day><hour>10:00</hour><dummy><dummy1>TTTTTTT</dummy1></dummy></serv_char><serv_char><day>FRIDAY</day><hour>23:00</hour><dummy><dummy1>QWERTY</dummy1></dummy></serv_char></service><service><code>SERVICE-02</code><description>BOH</description><serv_char><day>AFTER</day><hour>77:00</hour><dummy><dummy1>HIHIHI</dummy1></dummy></serv_char><serv_char><day>BEFORE</day><hour>11:00</hour><dummy><dummy1>UHUHUH</dummy1></dummy></serv_char><serv_char><day>NEVER</day><hour>88:00</hour><dummy><dummy1>AHAHAHA</dummy1></dummy></serv_char></service></dummy_serv></products></submitBasketRequest>
and his relative data structure:
DATA: BEGIN OF st_xml_prod_char,xml_color TYPE string,xml_size TYPE string,END OF st_xml_prod_char.DATA: BEGIN OF st_product,xml_code TYPE string,xml_description TYPE string,xml_quantity TYPE string,xml_prod_char LIKE TABLE OF st_xml_prod_char,END OF st_product.DATA: BEGIN OF st_xml_serv_char_dummy,xml_dummy1 TYPE string,END OF st_xml_serv_char_dummy.DATA: BEGIN OF st_xml_serv_char,xml_day TYPE string,xml_hour TYPE string,xml_dummy LIKE TABLE OF st_xml_serv_char_dummy,END OF st_xml_serv_char.DATA: BEGIN OF st_service,xml_code TYPE string,xml_description TYPE string,xml_serv_char LIKE TABLE OF st_xml_serv_char,END OF st_service.DATA: BEGIN OF st_dummy_serv,xml_service LIKE TABLE OF st_service,END OF st_dummy_serv.DATA: BEGIN OF st_products,xml_product LIKE TABLE OF st_product,xml_dummy_serv LIKE st_dummy_serv,END OF st_products.DATA: BEGIN OF abap_data,xml_products LIKE st_products,END OF abap_data.
In the next release of this code we plan:
- Attribute management
- Automatic code generation from XSD
Acknowledgment: Parts of this work is made by my best friend Paolo Bardelli.


