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 table
end 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_document
stream_factory = lo_factory
istream = 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_node
value(table_name)
value(abap_field_prefix)
CHANGING abap_data.
* Strutture ABAP
DATA: 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 XML
DATA: 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/Valore
DATA: 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_node
lv_field_name
abap_field_prefix
CHANGING <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.

Leave a Reply