Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
kkram
Contributor

<body><p>In this weblog, I am presenting a generic way to convert any nested complex XML structure to flat using ABAP mapping. This is especially useful for interfaces using receiver file adapter with content conversion.</p>

<p> </p>

<p>We know that the receiver file adapter with content conversion can only handle flat XML structure. That is, the resulting XML file should be of structure</p>

<p><root></p>

<p>  <nameA></p>

<p>     <value1>value</value1></p>

<p>     <value2>value</value2></p>

<p>     <value3>value</value3></p>

<p>  </nameA></p>

<p>  <nameB></p>

<p>     <value4>value</value4></p>

<p>  </nameB></p>

<p></root></p>

<p> </p>

<p>However, in most cases, we might have to handle complex nested structures in message mapping. This is especially true when the target flat file has complex structure with multiple layouts per line. </p>

For example, consider the below target XML message and flat file structure

<p> </p>

<p>Target XML structure (input to receiver file adapter)</p>

<p><root></p>

<p> <ORDERS></p>

<p>  <HEADER></p>

<p>    <HDR></HDR></p>

<p>      <FH1></FH1></p>

<p>      <FH2></FH2></p>

<p>  </HEADER></p>

<p> <DETAIL1></p>

<p>  <DET1></DET1></p>

<p>  <DF11></DF11></p>

<p>  <DF12></DF12></p>

<p>  </DETAIL1></p>

<p> <DETAIL2></p>

<p>  <DF21></DF21></p>

<p>  <DF22></DF22></p>

<p>  </DETAIL2></p>

<p> </ORDERS></p>

<p></root></p>

<p> </p>

<p>Target flat file layout</p>

<p>HDR,FH1,FH2…<br>

DET1,DF11,DF12<br>

DET2,DF21,DF22<br>

….<br>

HDR,FH1,FH2<br>

DET1,DF11,DF12<br>

DET2,DF21,DF22<br></p>

<p>

The file contains a group of three record types namely, Header, Detail1 and Detail2. Each record type has is own structure and the group repeats itself. For e.g., if this file should contain sales order information, the header contains order header information, Detail 1 contains line item information and Detail 2 contains schedule line information. There could be multiple sales orders as well</p>

<p> </p>

<p>We can derive the flat structure in graphical mapping but not without the risk of disturbing the context especially if the source structure is complex nested (for e.g. IDOC). In which case, all HDR lines would be grouped together followed by all DET1 lines, then DET2 line etc.</p>

<p> </p>

<p>We had many interfaces with such complex target structures. Instead of repeatedly complicating the graphical mapping, I decided to come up with a generic mapping which will convert any complex structure in to flat structure. This way, the graphical mapping is free of complex context handling functions. I chose to use ABAP mapping to achieve this as described below</p>

<p> </p>

<p>Input to the ABAP mapping has two parts in the XML document. <br>

  1.     One contains the actual target structure, which is complex nested <br>

  2.     Other part contains the nodes which need to be flattened. This also contains elements for target message name and namespace. The idea of this part is to make the ABAP mapping generic so as to use it with any interface<br></p>

<p> </p>

In our example the input to the ABAP mapping will look like as below

<p> </p>

<p><FLATFILE><br>

  <Z_FILTERS><br>

    <NAME>HEADER</NAME><br>

    <NAME>DETAIL1</NAME><br>

    <NAME>DETAIL2</NAME><br>

  </Z_FILTERS><br>

  <Z_TMSG_TYPE></Z_TMSG_TYPE><br>

  <ORDERS><br>

    <HEADER><br>

      <HDR></HDR><br>

      <FH1></FH1><br>

      <FH2></FH2><br>

      ..<br>

    </HEADER><br>

    <DETAIL1><br>

      <DET1></DET1><br>

      <DF11></DF11><br>

      <DF12></DF12><br>

      ..<br>

    </DETAIL1><br>

    <DETAIL2><br>

      <DF21></DF21><br>

      <DF22></DF22><br>

      ..<br>

    </DETAIL2><br>

  </ORDERS><br>

</FLATFILE><br></p>

<p> </p>

<p>The code for the ABAP mapping is provided below.</p>

<p>Method: IF_MAPPING~EXECUTE

In this method we parse the input XML using DOM and read through to get only the nodes which need to flattened out.

  • All data declarations

  TYPE-POOLS: IXML.

  CLASS CL_IXML DEFINITION LOAD.

  DATA: BEGIN OF WA_FILTERS,

          NAME TYPE STRING,

        END OF WA_FILTERS.

  DATA: R_XML_FACTORY TYPE REF TO IF_IXML,

        R_STREAM_FACTORY TYPE REF TO IF_IXML_STREAM_FACTORY,

        R_STREAM TYPE REF TO IF_IXML_ISTREAM,

        R_OSTREAM TYPE REF TO IF_IXML_OSTREAM,

        R_IN_DOC TYPE REF TO IF_IXML_DOCUMENT,

        R_OUT_DOC TYPE REF TO IF_IXML_DOCUMENT,

        R_PARSER TYPE REF TO IF_IXML_PARSER,

        R_ENCODING TYPE REF TO IF_IXML_ENCODING,

        R_RENDERER TYPE REF TO IF_IXML_RENDERER,

        R_ROOT TYPE REF TO IF_IXML_ELEMENT,

        R_OUT_ROOT TYPE REF TO IF_IXML_ELEMENT,

        R_FILTER_ELEMENT TYPE REF TO IF_IXML_ELEMENT,

        R_TMSG_ELEMENT TYPE REF TO IF_IXML_ELEMENT,

        R_FILTER_MAIN TYPE REF TO IF_IXML_NODE_FILTER,

        R_FILTER TYPE REF TO IF_IXML_NODE_FILTER,

        R_ITER TYPE REF TO IF_IXML_NODE_ITERATOR,

        R_CHILD TYPE REF TO IF_IXML_NODE,

        R_NODE TYPE REF TO IF_IXML_NODE,

        R_CLONED_NODE TYPE REF TO IF_IXML_NODE,

        T_FILTERS LIKE TABLE OF WA_FILTERS,

        V_RC TYPE I,

        V_ROOT_NAME TYPE STRING,

        V_ROOT_PREFIX TYPE STRING,

        V_ROOT_NS TYPE STRING,

        V_ROOT_URI TYPE STRING,

        V_SOURCE TYPE STRING.

  • Create Main factory class

  R_XML_FACTORY = CL_IXML=>CREATE( ).

  • Create stream fatory

  R_STREAM_FACTORY = R_XML_FACTORY->CREATE_STREAM_FACTORY( ).

  V_SOURCE = SOURCE.

  R_STREAM = R_STREAM_FACTORY->CREATE_ISTREAM_XSTRING( SOURCE ).

  • initialize input/output document

  R_IN_DOC = R_XML_FACTORY->CREATE_DOCUMENT( ).

  R_OUT_DOC = R_XML_FACTORY->CREATE_DOCUMENT( ).

  • parse input document

  R_PARSER = R_XML_FACTORY->CREATE_PARSER( STREAM_FACTORY = R_STREAM_FACTORY

                                           ISTREAM = R_STREAM

                                           DOCUMENT = R_IN_DOC ).

  R_PARSER->PARSE( ).

  • Get the encoding

  R_ENCODING = R_IN_DOC->GET_ENCODING( ).

  • Get the root element

  R_ROOT = R_IN_DOC->GET_ROOT_ELEMENT( ).

  • Get the filter objects first

  R_FILTER_ELEMENT = R_IN_DOC->FIND_FROM_NAME( 'Z_FILTERS' ).

  IF NOT R_FILTER_ELEMENT IS INITIAL.

  R_CHILD = R_FILTER_ELEMENT->GET_FIRST_CHILD( ).

  WHILE NOT R_CHILD IS INITIAL.

    CLEAR: WA_FILTERS.

    WA_FILTERS-NAME = R_CHILD->GET_VALUE( ).

    APPEND WA_FILTERS TO T_FILTERS.

    R_CHILD = R_CHILD->GET_NEXT( ).

  ENDWHILE.

  ENDIF.

    • Throw error if no filters are available.

  CLEAR: R_FILTER, R_FILTER_MAIN.

  LOOP AT T_FILTERS INTO WA_FILTERS.

    IF SY-TABIX = 1.

      R_FILTER_MAIN = R_IN_DOC->CREATE_FILTER_NAME( NAME = WA_FILTERS-NAME ).

    ELSE.

      R_FILTER = R_IN_DOC->CREATE_FILTER_NAME( NAME = WA_FILTERS-NAME ).

      R_FILTER_MAIN  = R_IN_DOC->CREATE_FILTER_OR( FILTER1 = R_FILTER_MAIN

                                                   FILTER2 = R_FILTER ).

    ENDIF.

  ENDLOOP.

  • Get the root node to the outbound document.

  • Get the target message name from the payload

  R_TMSG_ELEMENT = R_IN_DOC->FIND_FROM_NAME( 'Z_TMSG_TYPE' ).

  IF NOT R_TMSG_ELEMENT IS INITIAL.

    V_ROOT_NAME = R_TMSG_ELEMENT->GET_VALUE( ).

  ENDIF.

  • V_ROOT_NAME = R_ROOT->GET_NAME( ).

  V_ROOT_PREFIX = R_ROOT->GET_NAMESPACE_PREFIX( ).

  V_ROOT_URI = R_ROOT->GET_NAMESPACE_URI( ).

  R_OUT_ROOT = R_OUT_DOC->CREATE_SIMPLE_ELEMENT_NS( NAME = V_ROOT_NAME

                                                    PREFIX = V_ROOT_PREFIX

                                                    URI    = V_ROOT_URI

                                                    PARENT = R_OUT_DOC ).

  IF NOT V_ROOT_PREFIX IS INITIAL.

    CONCATENATE 'xmlns:' V_ROOT_PREFIX INTO V_ROOT_NS.

    V_RC = R_OUT_ROOT->SET_ATTRIBUTE( NAME   = V_ROOT_NS

                                      VALUE  = V_ROOT_URI ).

  ENDIF.

  V_RC = R_OUT_DOC->APPEND_CHILD( R_OUT_ROOT ).

  CLEAR: R_ITER.

  R_ITER = R_IN_DOC->CREATE_ITERATOR_FILTERED( R_FILTER_MAIN ).

  R_NODE = R_ITER->GET_NEXT( ).

  WHILE NOT R_NODE IS INITIAL.

    R_CLONED_NODE = R_NODE->CLONE( ).

    R_OUT_ROOT->APPEND_CHILD( R_CLONED_NODE ).

    R_NODE = R_ITER->GET_NEXT( ).

  ENDWHILE.

  • render document ======================================================

  • create output stream

  CLEAR: R_STREAM.

  R_OSTREAM = R_STREAM_FACTORY->CREATE_OSTREAM_XSTRING( RESULT ).

  • Set encoding

  R_OUT_DOC->SET_ENCODING( ENCODING = R_ENCODING ).

  • create renderer

  R_RENDERER = R_XML_FACTORY->CREATE_RENDERER( OSTREAM = R_OSTREAM

                                             DOCUMENT = R_OUT_DOC ).

  V_RC = R_RENDERER->RENDER( ).

Method: GET_LAST_NODE

This method is used to recursively to call itself till reference to the last child node is obtained.

DATA: CHILD TYPE REF TO IF_IXML_NODE,      NODENAME TYPE STRING.CHILD = IN_NODE->GET_FIRST_CHILD( ).IF CHILD IS INITIAL.  NODENAME = CHILD->GET_NAME( ).  OUT_NODE->APPEND_CHILD( IN_NODE ).ELSE.  WHILE NOT CHILD IS INITIAL.  NODENAME = CHILD->GET_NAME( ).  CALL METHOD ME->GET_LAST_NODE    EXPORTING      IN_NODE  = CHILD    CHANGING      OUT_NODE = OUT_NODE.  ENDWHILE.ENDIF.

The input parameters to method GET_LAST_NODE are as below

2 Comments
Labels in this area