Copyright 2024 - BV TallVision IT

XML has proven to be valuable in many automation quests, which did not go unnoticed with SAP folk. Hence the CALL TRANSFORMATION statement was introduced. And this is more than just a statement, it's a simple and effective way to compose AND/OR interpret XML content. 

What the transformation does is map fields/structures from Abap memory to an XML setup - and use this mapping to either move variables from Abap memory to the XML content, or the other way around. You are not just defining how the XML should be composed, you are automatically also defining how the XML should be interpreted/read. 

Transformations can be created/maintainted from Abap coding (through double click), or transaction STRANS can be used to access a transformation directly. Note that transformations have their own namespace and can be re-used in several Abap programs.

In abap coding this looks like so (compose an XML file with a structure and an internal table with my data): 

CALL TRANSFORMATION z_my_xml_transformation
  SOURCE
    header_info   = lw_my_xml_info
    t_item_info = lt_xml_info_table
  RESULT XML 
    lv_xml_result.

The lv_xml_result would typically be type string. Double click on the z_my_xml_transformation to create (or go into) the actual transformation. This would look something like this:

<?sap.transform simple?>
<tt:transform xmlns:tt="http://www.sap.com/transformation-templates">
  <tt:root name="ROOT"/>
  <tt:root name="HEADER_INFO"/>
  <tt:root name="T_ITEM_INFO"/>
  
  <tt:template>
    <my_xml>
      <header>
        <field1>
          <tt:value ref="HEADER_INFO.FIELD1"/>
        </field1>
        <field2>
          <tt:attribute name="priority" 
              value-ref="HEADER_INFO.ATTRIBUTE2"/>
          <tt:value ref="HEADER_INFO.FIELD2"/>
        </field2>
      </header>
      <items>
        <tt:loop name="items" ref="T_ITEM_INFO">
          <item>
            <itemfield1>
              <tt:value ref="$items.ITEMFIELD1"/>
            </itemfield1>
            <itemfield2>
              <tt:value ref="$items.ITEMFIELD2"/>
            </itemfield2>			
          </item>
	</tt:loop>
      </items>
    </my_xml>
  </tt:template>

</tt:transform>			

The above should render the following XML (in lv_xml_result):

<?xml version="1.0" encoding="utf-16"?>#<my_xml><header><field1>....

Which is of course not very readible to the human eye. Feed your string to the method shown below, and the output will be "pretty printed" like so:

<?xml version="1.0" encoding="utf-16"?>
<my_xml>
  <header>
    <field1>TICTOC</field1>
    <field2 priority="Low">TACTAC</field2>
  </header>
  <items>
    <item>
      <itemfield1>ABCDE</itemfield1>
      <itemfield2>CD123</itemfield2>
    </item>
    <item>
      <itemfield1>AXCDE</itemfield1>
      <itemfield2>CDX23</itemfield2>
    </item>
    <item>
      <itemfield1>ABXXE</itemfield1>
      <itemfield2>CX1XX</itemfield2>
    </item>
  </items>
</my_xml>

A method you could park on your system somewhere - for pretty printing XML output:

method XML_PRETTY_PRINTER.
*--------------------------------------------------------------------
* Method        : XML_PRETTY_PRINTER
* Title         : Formatting method to create readable XML output
* Author        : Wim Maasdam
* Description   : This method transforms a (long)string with XML
*                 data into a pretty-printed version of the same
*                 data. It effectively add's about 20% more bytes
*                 to the string to present the XML tags in a multi-
*                 line format.
*--------------------------------------------------------------------
* Change history:
*  date       change/error     Description
*--------------------------------------------------------------------
  DATA: l_cnt_length TYPE i,
        l_cnt_index TYPE i,
        l_xmlline TYPE string,
        l_oldchar TYPE c,
        l_char TYPE c,
        l_nextchar TYPE c,
        l_indent TYPE i.

  l_cnt_length = STRLEN( xml_string ).
  DO l_cnt_length TIMES.
    l_cnt_index = sy-index - 1.
    l_char = xml_string+l_cnt_index(1).
    IF sy-index < l_cnt_length.
      l_nextchar = xml_string+sy-index(1).
    ENDIF.
    IF  l_char <> cl_abap_char_utilities=>newline.

      IF ( l_char = '<' AND l_nextchar = '/' ) OR
         ( l_char = '/' AND l_nextchar = '>' ).
        l_indent = l_indent - 1.
        IF l_indent < 0.
          l_indent = 0.
        ENDIF.
      ENDIF.

      IF l_char = '<' AND l_oldchar = '>'.
        APPEND l_xmlline TO pretty_xml.
*      write: / l_xmlline.
        CLEAR l_xmlline.
        DO l_indent TIMES.
          CONCATENATE space space l_xmlline INTO l_xmlline 
            SEPARATED BY space.
        ENDDO.
      ENDIF.
      if l_char <> space.
        CONCATENATE l_xmlline l_char INTO l_xmlline.
      else.
        CONCATENATE l_xmlline l_char INTO l_xmlline 
          SEPARATED BY space.
      endif.

      IF l_char = '<' AND l_nextchar <> '/'.
        l_indent = l_indent + 1.
      ENDIF.

    ELSE.
      l_indent = 0.
      APPEND l_xmlline TO pretty_xml.
      CLEAR l_xmlline.
    ENDIF.
    l_oldchar = l_char.
  ENDDO.
  APPEND l_xmlline TO pretty_xml.

endmethod.