Skip to Content
Technical Articles
Author's profile photo Rasesh Thakkar

Filtering Data Dynamically in SAP CPI using XSLT Mapping.

Hello All,

Greetings for the Day!!

You may have encountered various scenarios where you configured more than three filters and created different paths yet performed the same task in all these paths. The first filter has the department equal to HR, while the others correspond to Finance, Sales, and Audit, respectively. Even though the data processing occurs in different paths, the same task is being executed in each path.

Complex%20Integration

Complex Integration

As you can see above, the integration appears complex. The architect responsible for the integration would be able to debug it, but it might be a bit time-consuming for other architects to understand the process of the above integration. Validating all the integration steps, by reviewing each one and either removing or adding filter criteria, would also increase the complexity of the integration.

It will also impact the performance of the integration, as it involves multiple steps that need to be executed. This will subsequently increase the processing time of the integration. The API calls made by the above integration will raise the load on the SuccessFactors system due to multiple API calls being made for performing the same task.

Simplified%20Integration

Simplified Integration

Solution:

To reduce the complexity of integration and decrease the processing time, as well as to alleviate the load on the SuccessFactors API, we can utilize the ‘XSLT Mapping’ function within the integration. This function enables us to filter data, map fields, and execute necessary distinct functions.

What is XSLT Mapping?

XSLT (Extensible Stylesheet Language Transformations) mapping in SAP CPI refers to the process of using XSLT to transform and map data between different formats during integration scenarios. XSLT is a language used to transform XML documents into various formats.

In the context of SAP CPI, XSLT mapping allows you to:

  1. Transform Data: Convert XML data from the source format to the target format using XSLT templates. This is particularly useful when you need to map data from one XML structure to another.
  2. Perform Complex Mapping: XSLT provides a powerful way to perform complex transformations, conditional mapping, and data enrichment during integration.

Overall, XSLT mapping is a powerful tool in SAP CPI that enables you to manage complex data transformations and mappings in integration scenarios, easing seamless communication between different systems with varying data formats.

Methodology:

In this scenario, we are going to filter out cost centers and calculate the sum of course costs for all the users, storing it as Total Cost. Generally, most customers have multiple cost centers within their organization, and many new cost centers are created and removed from the system at regular intervals.

Ideally, it is not feasible to have multiple filters and add them manually when new cost centers are created or deleted post Go-Live.

We are going to use “group by” function which is a mechanism in XSLT mapping that allows you to group and aggregate data based on specific criteria. This is particularly useful when you’re dealing with XML data and need to perform operations on grouped data sets.

Here’s how the “group by” function works in XSLT within the context of SAP CPI:

  1. Grouping Data : The “group by” function lets you group XML elements based on a common attribute or element value. This means that elements with the same value for the specified attribute or element are treated as a group.
  2. Aggregating Data : Once the data is grouped, you can use aggregate functions (like sum, count, average, etc.) to perform calculations on the elements within each group. This allows you to calculate values for each group separately.
  3. Output Transformation : After grouping and aggregating, you can define how the resulting grouped and aggregated data should be transformed into the desired output format.

Here’s a simplified example of how you might use the “group by” function in XSLT within SAP CPI:

<!-- Group employees by Cost Center -->
<xsl:for-each-group select="{Entity Name} /{Entity Name}" group-by="{Enter Field to Filter}">
                <CostCenter ><xsl:value-of select="current-grouping-key()" /></CostCenter>
<!-- Calculate the Total Cost within the Cost Center -->
                <Total_Cost><xsl:value-of select="sum(current-group()/{Enter Field to Sum})"/></Total_Cost>

Suppose you have the below input XML data having Cost Center Details:

Input XML :
<CostDetails>
    <CostDetails>
        <externalCode>1982</externalCode>
        <cust_costcenter>3748</cust_costcenter>
        <course_cost>960</course_cost>
    </CostDetails>
    <CostDetails>
        <externalCode>1984</externalCode>
        <cust_costcenter>3748</cust_costcenter>
        <course_cost>850</course_cost>
    </CostDetails>
    <CostDetails>
        <externalCode>1986</externalCode>
        <cust_costcenter>3746</cust_costcenter>
        <course_cost>740</course_cost>
    </CostDetails>
    <CostDetails>
        <externalCode>1988</externalCode>
        <cust_costcenter>3746</cust_costcenter>
        <course_cost>630</course_cost>
    </CostDetails>
</CostDetails>

If you want to group the cost centers and calculate the cost of the course for each user, you could use the following XSLT code for your requirements.

Code :
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <output>
            <xsl:for-each-group select="CostDetails/CostDetails " group-by="cust_costcenter">
                <Center><xsl:value-of select="current-grouping-key()"/></Center>
                <Total_Cost><xsl:value-of select="sum(current-group()/course_cost)"/></Total_Cost>
                <employees>
                    <xsl:for-each select="current-group()">
                        <employee>
                            <userId><xsl:value-of select="externalCode "/></userId>
                            <cost><xsl:value-of select="course_cost"/></cost>
                            <costcenter><xsl:value-of select="cust_costcenter"/></costcenter>
                        </employee>
                    </xsl:for-each>
                </employees>
            </xsl:for-each-group>
        </output>
    </xsl:template>
</xsl:stylesheet>	

The response payload below has filtered 2 cost centers, calculated the cost of the course, and provided us with the required output.

Output XML :
<?xml version="1.0" encoding="UTF-8"?>
<output>
    <Center>3748</Center>
    <Total_Cost>1810</Total_Cost>
    <employees>
        <employee>
            <userId>1982</userId>
            <cost>960</cost>
            <costcenter>3748</costcenter>  
        </employee>
        <employee>
            <userId>1984</userId>
            <cost>850</cost>
            <costcenter>3748</costcenter>   
        </employee>
    </employees>
    <Center>3746</Center>
    <Total_Cost>1370</Total_Cost>
    <employees>
        <employee>
            <userId>1986</userId>
            <cost>740</cost>
            <costcenter>3746</costcenter>   
        </employee>
        <employee>
            <userId>1988</userId>
            <cost>630</cost>
            <costcenter>3746</costcenter>
        </employee>
    </employees>
</output>

Scenario 2:

If you want to apply a filter within another filter, you can perform a ‘group by’ operation on both the cost center and status in XSLT mapping. To achieve this, you can utilize nested ‘<xsl:for-each-group>’ elements for each level of grouping.

<!-- Within each Cost Center, group by Status -->
          <xsl:for-each-group select="current-group()" group-by="{Enter Field to Filter}">
                    <Status><xsl:value-of select="current-grouping-key()" /></Status>

Suppose you have the below input XML data having Cost Center Details and Status:

Input XML :
<CostDetails>
    <CostDetails>
        <externalCode>1982</externalCode>
        <course_status>Active</course_status>
        <cust_costcenter>3748</cust_costcenter>
        <course_cost>960</course_cost>
    </CostDetails>
    <CostDetails>
        <externalCode>1984</externalCode>
        <course_status>Active</course_status>
        <cust_costcenter>3748</cust_costcenter>
        <course_cost>550</course_cost>
    </CostDetails>
    <CostDetails>
        <externalCode>1981</externalCode>
        <course_status>Completed</course_status>
        <cust_costcenter>3748</cust_costcenter>
        <course_cost>950</course_cost>
    </CostDetails>
    <CostDetails>
        <externalCode>1983</externalCode>
        <course_status>Completed</course_status>
        <cust_costcenter>3748</cust_costcenter>
        <course_cost>250</course_cost>
    </CostDetails>
    <CostDetails>
        <externalCode>1986</externalCode>
        <course_status>Completed</course_status>
        <cust_costcenter>3746</cust_costcenter>
        <course_cost>740</course_cost>
    </CostDetails>
    <CostDetails>
        <externalCode>1988</externalCode>
        <course_status>Completed</course_status>
        <cust_costcenter>3746</cust_costcenter>
        <course_cost>630</course_cost>
    </CostDetails>
    <CostDetails>
        <externalCode>1987</externalCode>
        <course_status>Active</course_status>
        <cust_costcenter>3746</cust_costcenter>
        <course_cost>330</course_cost>
    </CostDetails>
    <CostDetails>
        <externalCode>1989</externalCode>
        <course_status>Active</course_status>
        <cust_costcenter>3746</cust_costcenter>
        <course_cost>430</course_cost>
    </CostDetails>
</CostDetails>

The XSLT code will group employees first by cost center and then within each cost center, group them by status. It calculates the total cost for each status within the cost center and outputs a structured XML accordingly.

Code :
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <output>
            <xsl:for-each-group select="CostDetails/CostDetails " group-by="cust_costcenter">
                <Center><xsl:value-of select="current-grouping-key()"/></Center>
                <xsl:for-each-group select="current-group()" group-by="course_status">
                    <Status><xsl:value-of select="current-grouping-key()"/></Status>
                    <Total_Cost><xsl:value-of select="sum(current-group()/course_cost)"/></Total_Cost>
                    <employees>
                        <xsl:for-each select="current-group()">
                            <employee>
                                <userId><xsl:value-of select="externalCode "/></userId>
                                <cost><xsl:value-of select="course_cost"/></cost>
                                <costcenter><xsl:value-of select="cust_costcenter"/></costcenter>
                                <status><xsl:value-of select="course_status"/></status>
                            </employee>
                        </xsl:for-each>
                    </employees>
                </xsl:for-each-group>
            </xsl:for-each-group>
        </output>
    </xsl:template>
</xsl:stylesheet>

As you can see below, the XSLT performed the ‘group by’ function for both the Cost Center and Status successfully.

Output XML :
<?xml version="1.0" encoding="UTF-8"?>
<output>
    <Center>3748</Center>
    <Status>Active</Status>
    <Total_Cost>1510</Total_Cost>
    <employees>
        <employee>
            <userId>1982</userId>
            <cost>960</cost>
            <costcenter>3748</costcenter>
            <status>Active</status>
        </employee>
        <employee>
            <userId>1984</userId>
            <cost>550</cost>
            <costcenter>3748</costcenter>
            <status>Active</status>
        </employee>
    </employees>
    <Status>Completed</Status>
    <Total_Cost>1200</Total_Cost>
    <employees>
        <employee>
            <userId>1981</userId>
            <cost>950</cost>
            <costcenter>3748</costcenter>
            <status>Completed</status>
        </employee>
        <employee>
            <userId>1983</userId>
            <cost>250</cost>
            <costcenter>3748</costcenter>
            <status>Completed</status>
        </employee>
    </employees>
    <Center>3746</Center>
    <Status>Completed</Status>
    <Total_Cost>1370</Total_Cost>
    <employees>
        <employee>
            <userId>1986</userId>
            <cost>740</cost>
            <costcenter>3746</costcenter>
            <status>Completed</status>
        </employee>
        <employee>
            <userId>1988</userId>
            <cost>630</cost>
            <costcenter>3746</costcenter>
            <status>Completed</status>
        </employee>
    </employees>
    <Status>Active</Status>
    <Total_Cost>760</Total_Cost>
    <employees>
        <employee>
            <userId>1987</userId>
            <cost>330</cost>
            <costcenter>3746</costcenter>
            <status>Active</status>
        </employee>
        <employee>
            <userId>1989</userId>
            <cost>430</cost>
            <costcenter>3746</costcenter>
            <status>Active</status>
        </employee>
    </employees>
</output>

Thank you for reading the blog. I request your feedback on the above configuration and code. Kindly let me know in case of any limitations that I can try to overcome.

Best Regards,
Rasesh Thakkar.

 

Assigned Tags

      4 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo vinay mittal
      vinay mittal

      Hello

       

      If you had used Gather immediately after Join as its recommended by SAP then you would have made just one call to the API.

      You have not used Gather with a multicast because of which the different exchange messages are never combined and spent memory is not recovered immediately after the processing ends.

      Also if there are common steps then again its a recommendation to use local Integration process and dump the common steps there and call them when needed. We wouldn't have needed the simplified version and could have avoided the additional complexity introduced by XSLT mapping if best practices were followed.

       

      Regards

      Vinay

      Author's profile photo Rasesh Thakkar
      Rasesh Thakkar
      Blog Post Author

      Hi Vinay,

      Thank you for your input.

      As mentioned in the blog title, "Filtering Data Dynamically in SAP CPI using XSLT Mapping," the primary objective of this blog is to demonstrate dynamic filtering in CPI. Although we do have a function available, it's a static method that can increase the complexity and processing steps of the iFlow. This is why we've introduced XSLT to dynamically filter specific fields. You can explore the examples provided in the blog to gain a better understanding.

      Additionally, we are aware of the recommended SAP best practices involving Join and Gather operations. If you've reviewed the Complex Integration Image, please note that it was merely an illustrative example of the approach used in my scenario.

      Let's connect if you have a better approach to filter data dynamically.

      Happy Learning!

      Best Regards,
      Rasesh Thakkar.

       

      Author's profile photo vinay mittal
      vinay mittal

      Hello Rasesh

       

      Your "merely illustrative example" in itself creates the problems that your simplified approach tries to solve. Also whenever using a iflow in a blog we need to ensure it follows best practices as even if someone wanted to use that first approach they shouldn't have ignored the gather step. 

      You have spoken here about dynamic filtering yet the XSLT mapping accepts hardcoded list of cost centres rather than a configurable parameter.

       

      Basically the simplified approach can be easily achieved by a message mapping that accepts a configurable value and using an XSLT mapping like you have used is also fine. But the complex architecture that you have described shouldnt have been used (or no one would use) in first place.

       

      There can be 10's of cost centres and they will increase in number every year probably or some will get deprecated or discontinued, It makes more sense to automatically sort these in the message mapping or xslt mapping and then group them WITHOUT hardcoding their id's, A simple sort via a script that groups identical cost centres data would have done the job without hardcoding anything.

       

      Regards

      Vinay

       

       

      Author's profile photo Rasesh Thakkar
      Rasesh Thakkar
      Blog Post Author

      Hi Vinay,

      I completely agree with your observation regarding the mistake you pointed out in the Complex Integration Image. Thank you for bringing it to my attention.

      You are absolutely correct; no one would typically use the architecture of the complex integration. My sole purpose in mentioning it was to illustrate that my XSLT Code can reduce the four paths created to just one.

      Furthermore, I haven't hardcoded the cost center values. If I had, there would be no point in writing this blog in the first place. I kindly request that you read and try my method. If you can enhance the method to make it more efficient for our readers, it would be greatly appreciated.

      Yes, it's possible to achieve this using Message Mapping. Personally, I tend to avoid it because it doesn't provide as much control over the data and logic as XSLT does. With XSLT, I have more freedom to manipulate both the data and the logic.

      My code also supports the addition and deletion of cost centers in the future. If a cost center is removed, it won't be assigned to any user; it will be skipped by default. Conversely, any newly added cost centers will already be assigned to the user and will be filtered by the integration.

      Thank you for your feedback. I genuinely appreciate you pointing out a mistake in my blog, and I will certainly ensure that I follow best practices in my future blogs as well!

      Happy Learning!

      Best Regards,

      Rasesh Thakkar.