Skip to Content
Technical Articles
Author's profile photo Former Member

How to process dynamic XSLT in ABAP

In the below post you will find a basic example how to use cl_xslt_processor library. However it is really poorly documented, so I decided to dive deeper, test it a little bit and let you know how to use it in more details.

Backstory

Lately I have encountered a small problem. We wanted to develop an interface, which would consume a simple (not really) web service. Easy, right? You just save necessary SOAP envelopes, set headers and send HTTP request.

Yes, but we wanted to make this development reusable and scalable in order to consume not yet known web services in the future. What if we could modify request body (XML) from the outside without modifying our code? Set descriptions and other fields in the request any way we wanted, using data available in the program?

Generating XML from modifable XSLT would allow us to achieve everythinfg described above.

I started digging and found this trace on the web – https://archive.sap.com/discussions/thread/947687  – trace of cl_xslt_processor class.

In the above post you will find a basic example how to use this library. However it is really poorly documented, so I decided to dive deeper, test it a little bit and let you know how to use it in more details.

Implementation

When you look at the class definition you will see two main groups of methods – set_source* and set_result* :

Set_source* sets incoming message to be transformed, while set_result* points to the result object after transformation. In this example I will be using objects of well known iXML library.

Source XML:

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="example.xsl"?>
<Article>
  <Title>My Article</Title>
  <Authors>
    <Author>Mr. Foo</Author>
    <Author>Mr. Bar</Author>
  </Authors>
  <Body>This is my article text.</Body>
</Article>

XSLT (examples taken from Mozilla webservice):

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:template match="/">
    Article - <xsl:value-of select="/Article/Title"/>
    Authors: <xsl:apply-templates select="/Article/Authors/Author"/>
  </xsl:template>

  <xsl:template match="Author">
    - <xsl:value-of select="." />
  </xsl:template>

</xsl:stylesheet>

First we need to create iXML objects:

DATA(lo_xml) = cl_ixml=>create( ).
DATA(lo_source_document) = lo_xml->create_document( ).
DATA(lo_result_document) = lo_xml->create_document( ).
DATA(lo_stream_factory) = lo_xml->create_stream_factory( ).
DATA(lo_xml_istream) = lo_stream_factory->create_istream_string( xml_string ).
DATA(lo_xslt_istream) = lo_stream_factory->create_istream_string( xslt ).
DATA(lo_parser) = lo_xml->create_parser( document       = lo_source_document
                                         istream        = lo_xml_istream
                                         stream_factory = lo_stream_factory ).
lo_parser->parse( ).

As you can see lo_source_document represents source XML under variable xml_string, while lo_result_document is an empty document. lo_xslt_stream will be used as a source stream in cl_xslt_processor.

DATA(lo_xslt_processor) = NEW cl_xslt_processor( ).
TRY.
    lo_xslt_processor->set_source_node( lo_source_document ).
    lo_xslt_processor->set_result_document( lo_result_document ).
    lo_xslt_processor->set_source_stream( EXPORTING stream = lo_xslt_istream
                                          CHANGING p = lv_progname_string ).
    lo_xslt_processor->run( space ).
  CATCH cx_xslt_exception.

ENDTRY.

We set source and result document, XSLT istream from prevoius step and run.

Quick way to display:

DATA(lo_cl_xml) = NEW cl_xml_document( ).
lo_cl_xml->create_with_dom( lo_result_document ).
lo_cl_xml->render_2_string(
  EXPORTING
    pretty_print = 'X'
  IMPORTING
    stream       = DATA(lv_string)
).

 

Result

Check the content of final string:

Hooray!

Below you can find the whole code and test it on your own:

DATA: lv_progname_string TYPE string VALUE abap_true.

DATA(xml_string) =  |<?xml version="1.0"?>| &&
                    |<?xml-stylesheet type="text/xsl" href="example.xsl"?>| &&
                    |<Article>| &&
                    |    <Title>My Article</Title>| &&
                    |    <Authors>| &&
                    |        <Author>Mr. Foo</Author>| &&
                    |        <Author>Mr. Bar</Author>| &&
                    |    </Authors>| &&
                    |    <Body>This is my article text.</Body>| &&
                    |</Article>|.


DATA(xslt) = |<?xml version="1.0"?>| &&
             |<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">| &&
             |  <xsl:output method="text"/>| &&
             |  <xsl:template match="/">| &&
             |      Article - <xsl:value-of select="/Article/Title"/>| &&
             |      Authors: <xsl:apply-templates select="/Article/Authors/Author"/>| &&
             |  </xsl:template>| &&
             |  <xsl:template match="Author">| &&
             |      - <xsl:value-of select="." />| &&
             |  </xsl:template>| &&
             |</xsl:stylesheet>|.



DATA(lo_xml) = cl_ixml=>create( ).
DATA(lo_source_document) = lo_xml->create_document( ).
DATA(lo_result_document) = lo_xml->create_document( ).
DATA(lo_stream_factory) = lo_xml->create_stream_factory( ).
DATA(lo_xml_istream) = lo_stream_factory->create_istream_string( xml_string ).
DATA(lo_xslt_istream) = lo_stream_factory->create_istream_string( xslt ).
DATA(lo_parser) = lo_xml->create_parser( document       = lo_source_document
                                         istream        = lo_xml_istream
                                         stream_factory = lo_stream_factory ).
lo_parser->parse( ).

DATA(lo_xslt_processor) = NEW cl_xslt_processor( ).

TRY.
    lo_xslt_processor->set_source_node( lo_source_document ).
    lo_xslt_processor->set_result_document( lo_result_document ).
    lo_xslt_processor->set_source_stream( EXPORTING stream = lo_xslt_istream
                                          CHANGING p = lv_progname_string ).
    lo_xslt_processor->run( space ).
  CATCH cx_xslt_exception.

ENDTRY.

DATA(lo_cl_xml) = NEW cl_xml_document( ).
lo_cl_xml->create_with_dom( lo_result_document ).
lo_cl_xml->display( ).
lo_cl_xml->render_2_string(
  EXPORTING
    pretty_print = 'X'
  IMPORTING
    stream       = DATA(lv_string)
).

WRITE lv_string.

 

What is your experience with XSLT in ABAP?

Assigned Tags

      3 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Sandra Rossi
      Sandra Rossi

      I use only XSLT when there's a very complex transformation. For most of cases, I use ST as I had measured a 10-times better performance, a few years ago. I also prefer using CALL TRANSFORMATION, usually no need of all this iXML stuff. There's also an intermediate sXML library which is useful for reading or generating JSON very simply for instance.

      Author's profile photo Ernest Kusmierz
      Ernest Kusmierz

      great article!

      Author's profile photo genia scott
      genia scott

      Excellent Article!!!