SAP CPI: Three easy ways to sort XML

Int4 Team
2020-10-27

In this article you will learn:

  • How to sort XML messages without rebuilding them
  • What are the differences between XSLT 1.0 and XSLT 3.0 sorting capabilities
  • How to use Groovy XmlParser to sort XML without building it from scratch[/list]

Reading time: 5 minutes

SAP CPI: Three easy ways to sort XML

Introduction

Sorting XML messages is a common task. You can encounter it at many different occasions working with big data sets that arrive at the middleware platform in a bit unorganized way or when the way of sorting data is much different on the receiving side of the interface, then on the sending part. In all of these cases you can either implement sorting along with the structure mapping (complex), but you can also choose my preferred way to sort incoming data prior to the main structure mapping, to keep things simple and clear.

When you decide to sort your XML without changing its structure, the go to method should be something that doesn’t require rebuilding XML field by field, but either allows operation on a whole message at once. Using SAP CPI you have two options to choose from, either XSLT or Groovy. I will present in this blog both XSLT 1.0 and XSLT 3.0 approach, as you might find XSLT 1.0 useful for your SAP PO implementation.

To showcase all three sorting options XSLT 1.0, XSLT 3.0 and Groovy, I will use below XML:

Language XML:

<List>

   <Test>

       <Person>

           <Number>5</Number>

           <Control>

               <Level2>5</Level2>

           </Control>

       </Person>

       <Person>

           <Number>7</Number>

           <Control>

               <Level2>7</Level2>

           </Control>

       </Person>

       <Person>

           <Number>2</Number>

           <Control>

               <Level2>2</Level2>

           </Control>

       </Person>

       <Person>

           <Number>4</Number>

           <Control>

               <Level2>4</Level2>

           </Control>

       </Person>

   </Test>

   <Test>

       <Person>

           <Number>8</Number>

           <Control>

               <Level2>8</Level2>

           </Control>

       </Person>

       <Person>

           <Number>1</Number>

           <Control>

               <Level2>1</Level2>

           </Control>

       </Person>

       <Person>

           <Number>4</Number>

           <Control>

               <Level2>4</Level2>

           </Control>

       </Person>

       <Person>

           <Number>6</Number>

           <Control>

               <Level2>6</Level2>

           </Control>

       </Person>

       <Person>

           <Number>2</Number>

           <Control>

               <Level2>2</Level2>

           </Control>

       </Person>

   </Test>

</List>

Code 1: XML Input

Please, notice that this XML is a bit more complicated than usual sorting examples, as sorting in real life almost never applies to flat like XMLs, but to far more deep structures. This XML will allow understanding in more detail what is really happening behind the scene of those transformations. Goal of this sorting will be to sort Person by Number but only within a single Test. Important part is to move the whole Person when sorting with all its children. Expected result is presented below:

Language XML:

<List>

  <Test>

    <Person>

      <Number>2</Number>

      <Control>

        <Level2>2</Level2>

      </Control>

    </Person>

    <Person>

      <Number>4</Number>

      <Control>

        <Level2>4</Level2>

      </Control>

    </Person>

    <Person>

      <Number>5</Number>

      <Control>

        <Level2>5</Level2>

      </Control>

    </Person>

    <Person>

      <Number>7</Number>

      <Control>

        <Level2>7</Level2>

      </Control>

    </Person>

  </Test>

  <Test>

    <Person>

      <Number>1</Number>

      <Control>

        <Level2>1</Level2>

      </Control>

    </Person>

    <Person>

      <Number>2</Number>

      <Control>

        <Level2>2</Level2>

      </Control>

    </Person>

    <Person>

      <Number>4</Number>

      <Control>

        <Level2>4</Level2>

      </Control>

    </Person>

    <Person>

      <Number>6</Number>

      <Control>

        <Level2>6</Level2>

      </Control>

    </Person>

    <Person>

      <Number>8</Number>

      <Control>

        <Level2>8</Level2>

      </Control>

    </Person>

  </Test>

</List>

Code 2: XML Output

XSLT 1.0

Using XSLT 1.0 to sort XML files is the oldest but still fully functional and simple method of sorting. It will also work in ABAP and SAP PO, so it’s still important for you to be aware of how to use it.

Language XML:

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

 <xsl:output omit-xml-declaration="yes" indent="yes"/>




 <xsl:template match="node()|@*">

     <xsl:copy>

       <xsl:apply-templates select="@*|node()"/>

     </xsl:copy>

 </xsl:template>




 <xsl:template match="Test">

  <xsl:copy>

    <xsl:apply-templates select="@*"/>

    <xsl:apply-templates select="Person">

      <xsl:sort select="Number" data-type="number" order="ascending"/>

    </xsl:apply-templates>

  </xsl:copy>

 </xsl:template>

</xsl:stylesheet>

Code 3: XSLT 1.0 Sort

Above code, use the first template to copy all the nodes that you are not interested in sorting, then it is starting to work on the Test. Because you have two Test those sortings will apply separately, so there will be no issue with sort overlapping other Test. In <xsl:sort> under select you specify which node should be the key used to sort Person.

XSLT 3.0

SAP CPI allows you to work with XSLT 3.0 and what it brings to the table is the XPATH 3.1 and all its functions from fn namespace. One of them, the new XPATH 3.1 addition is fn:sort. Which makes sorting even simpler and in specific use cases much more powerful, then XSLT 1.0 way of sorting.

Language XML:

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/2005/xpath-functions">

 <xsl:output omit-xml-declaration="yes" indent="yes"/>




 <xsl:template match="node()|@*">

     <xsl:copy>

       <xsl:apply-templates select="@*|node()"/>

     </xsl:copy>

 </xsl:template>




 <xsl:template match="Test">

    <xsl:copy>

        <xsl:apply-templates select="@*"/>

        <xsl:copy-of select="fn:sort(Person, (), function($funct) {$funct/Number})"/>

    </xsl:copy>

 </xsl:template>

</xsl:stylesheet>

Code 4: XSLT 3.0 Sort

This example is very similar to what you can see for XSLT 1.0, but instead of using <xsl:sort>, fn:sort is used as a function supplying select in the <xsl:copy-of> statement. function defined in fn:sort can be as complex as you like, so you can consider this method when your sorting key is not so obvious. Real life  example, you can sort by total weight of the package when you only have quantities and unit weights of what is in the package, as that will allow you to calculate the key on the fly.

Groovy

Last but not least, Groovy Script. Because it’s a script, you for sure assume that Groovy can do that too. You are right of course.

Language Groovy:

import com.sap.gateway.ip.core.customdev.util.Message

import groovy.xml.XmlUtil

def Message processData(Message message) {

   def body = message.getBody(Reader)

   def rootNode = new XmlParser().parse(body)

   rootNode.Test.each { it.children().sort { it.Number.text() } }

   message.setBody( XmlUtil.serialize(rootNode) )

   return message

}

Code 5: Groovy sort

When you are working with XML that you would like to change, Groovy offers you XmlParser. This parsing method allows in-place manipulation of parsed messages. What it does is very close to what XSLT is doing. You just split processing on the Test level using each and then you are working on single Test’s children, so Person, you are sorting them by Number. What is important, because you used XmlParser, this operation is happening on the Node object, so you can simply serialize it back to XML and have your result.

Easy right?

As you can see, those sorting code snippets are simple, but powerful at the same time. They are leveraging capabilities of CPI transformations, that can work on whole files at once. What is more, excluding sorting from main mappings is from my personal view much safer. Not only, you sort your message and you are able to check that sort separately, but you also provide your main mapping with already prepared data, which should be easier to process.

I strongly encourage you to try out those methods and see which works best for you.

Read also:

1. SAP CPI: Technical guide to build a single XSLT mapping for multiple input types