Technical Articles
SAP Cloud Platform Integration (CPI): Use cases of node functions in message mapping
Introduction
For those who have background in SAP PI/PO should have no problem working on the mapping tools in SAP Cloud Platform Integration (CPI) since the tools are similar. On the other hand, for newcomers, they may struggle with the mapping tools especially the node functions.
Therefore, in this blog, I’ll focus only on the node functions because sometimes it’s difficult to understand comparing to the rest of standard functions in graphical mapping.
The contents and configurations are based on the real integration scenarios that I i have been facing in my prior/current projects. So you can read it through and apply to your integration scenarios as needed.
Table of Contexts
-
Context
-
Data Sorting
-
Convert a flat structure to a tree structure
-
Convert a tree structure to a flat structure (Case 1)
-
Convert a tree structure to a flat structure (Case 2)
Context
First of all, you have to understand what a context is.
SAP CPI handles message mappings as XML messages in queues. The queue contains all source data in XML format. In queue, ะ้ำ elements under the same parent node are considered as the same context. Whenever the parent node is changed, the context change is put into the queue (as highlighted in dark gray color).
For example, an employee has multiple positions at a company. And let use the following XML structure/data as an example.
<?xml version="1.0" encoding="UTF-8"?>
<Employees>
<Employee>
<PERNR>1000</PERNR>
<Name>Mr. A</Name>
<Position>
<PositionID>300</PositionID>
<PositionName>Position A</PositionName>
</Position>
<Position>
<PositionID>400</PositionID>
<PositionName>Position B</PositionName>
</Position>
<Position>
<PositionID>500</PositionID>
<PositionName>Position C</PositionName>
</Position>
<Position>
<PositionID>600</PositionID>
<PositionName>Position D</PositionName>
</Position>
<Position>
<PositionID>700</PositionID>
<PositionName>Position G</PositionName>
</Position>
</Employee>
</Employees>
The “PositionName” value of each “Position” element is assigned to separate contexts. Or we can also say that the context changes are based on the “Position” element.
How do we know which context we are using? You can click on the source field and select the contexts menu as shown below.
Now we have an option which we can have a different context change based on different level of higher element.
For example, instead of having “Position” context changes, we can change it to the “Employee” element.
As a result, the “PositionName” values of each “Employee” element are put in the same context. The context changes are now based on the “Employee” element.
Data Sorting
Scenario: Data sorting based on the dates
Integration scenario: To sort the employee’s job information based on latest dates
Mapping functions: removeContexts, dateTrans, sort, sortByKey, splitByValue
Based on the source data below, what we are trying to achieve is to sort the employee’s job information as shown in the target output. As well, we are going to convert the date format from YYYY-MM-DD to YYYYMMDD.
The overview of message mappings look like this. The “Employee”/”employment_information”/”job_information” elements can use direct mappings.
Now let focus on the “start_date” and “end_date” fields (which should have the same mapping pattern).
We use the following mapping functions to accomplish the end result.
- removeContext: To remove all context changes and put the dates in the same context so we can sort them.
- dateTrans: This step is straightforward. It’s used to convert the date format from YYYY-MM-DD to YYYYMMDD.
- sort: Set the sort order as descending.
- splitByValue: It’s simply to insert context changes based on each value changes by default.
Finally, the field mapping of position value will be as follows.
- removeContext & dateTrans: Same as above.
- sortByKey: It basically sorts “position” values based on the above “start_date” as a key field
- splitByValue: Same as above.
After you run the testing, you should get the result as shown in the target output.
Source:
<Employee>
<employment_information>
<job_information>
<start_date>2010-01-01</start_date>
<end_date>2010-12-31</end_date>
<position>Position A</position>
</job_information>
<job_information>
<start_date>2019-01-01</start_date>
<end_date>9999-12-31</end_date>
<position>Position C</position>
</job_information>
<job_information>
<start_date>2011-01-01</start_date>
<end_date>2018-12-31</end_date>
<position>Position B</position>
</job_information>
</employment_information>
</Employee>
Target:
<Employee>
<employment_information>
<job_information>
<start_date>2019-01-01</start_date>
<end_date>9999-12-31</end_date>
<position>Position C</position>
</job_information>
<job_information>
<start_date>2011-01-01</start_date>
<end_date>2018-12-31</end_date>
<position>Position B</position>
</job_information>
<job_information>
<start_date>2010-01-01</start_date>
<end_date>2010-12-31</end_date>
<position>Position A</position>
</job_information>
</employment_information>
</Employee>
Convert a flat structure to a tree structure
Scenario: To convert a flat structure to a tree structure
Integration scenario: Put subordinate employees under the same manager
Mapping functions: removeContexts, collapseContext, splitByValue, formatByExample
For example, Manager A (ID 1000) has 2 subordinates—Emp A & Emp B. And Manager B (ID 2000) also has 2 subordinates—Emp C & Emp D. So we are going to put those related subordinates under the same manager in a target structure.
The root element—CompoundEmployee—uses a direct mapping.
The “Person” element in the target structure will be grouped based on “Manager_ID”.
- removeContext: To remove all context changes and put all manager IDs in the same context.
- splitByValue: We choose the context change based on value change so the output is separated by distinct value of input.
- collapseContext: Basically it takes the first value of each context.
The “Manager ID” and “Name” values will use the same mapping pattern where unique values will be inserted into a target structure.
- removeContext: To remove all context changes and put all manager IDs in the same context.
- splitByValue: We choose the context change based on value change so the output is separated by distinct value of input.
- collapseContext: It gets the first value of each context from input to output.
- splitByValue: Now we change the context change based on each value. As a result, the context change is inserted based on each value changes.
The “Employee” elements are determined by numbers of subordinates of each manager.
- removeContext: To remove all context changes and put all manager IDs in the same context.
- splitByValue: The context change is based on value change so the output is separated by unique inout values.
- formatByExample: It takes the input values based on the context change from the pattern queue (second queue).
The “Employee ID” & “Name” values use direct mappings. Because the context change from the “Employee” element will also apply to these fields.
Here is the outcome.
Source:
<CompoundEmployee>
<Person>
<Manager_ID>1000</Manager_ID>
<Manager_Name>Manager A</Manager_Name>
<Employee_ID>8000</Employee_ID>
<Employee_Name>Emp A</Employee_Name>
</Person>
<Person>
<Manager_ID>1000</Manager_ID>
<Manager_Name>Manager A</Manager_Name>
<Employee_ID>8005</Employee_ID>
<Employee_Name>Emp B</Employee_Name>
</Person>
<Person>
<Manager_ID>2000</Manager_ID>
<Manager_Name>Manager B</Manager_Name>
<Employee_ID>9000</Employee_ID>
<Employee_Name>Emp C</Employee_Name>
</Person>
<Person>
<Manager_ID>2000</Manager_ID>
<Manager_Name>Manager B</Manager_Name>
<Employee_ID>9002</Employee_ID>
<Employee_Name>Emp D</Employee_Name>
</Person>
</CompoundEmployee>
Target:
<CompoundEmployee>
<Person>
<Manager_ID>1000</Manager_ID>
<Manager_Name>Manager A</Manager_Name>
<Employee>
<Employee_ID>8000</Employee_ID>
<Employee_Name>Emp A</Employee_Name>
</Employee>
<Employee>
<Employee_ID>8005</Employee_ID>
<Employee_Name>Emp B</Employee_Name>
</Employee>
</Person>
<Person>
<Manager_ID>2000</Manager_ID>
<Manager_Name>Manager B</Manager_Name>
<Employee>
<Employee_ID>9000</Employee_ID>
<Employee_Name>Emp C</Employee_Name>
</Employee>
<Employee>
<Employee_ID>9003</Employee_ID>
<Employee_Name>Emp D</Employee_Name>
</Employee>
</Person>
</CompoundEmployee>
Convert a tree structure to a flat structure (Case 1)
Scenario: To convert a tree structure to a flat structure
Integration scenario: To have only the employee’s latest position (first value from each element)
Mapping functions: collapseContext, splitByValue
Assuming that the employee’s positions have already been sorted descending. So we will take only current positions in the output structure.
The mapping of “Employees”/”Employee”/”PERNR”/”Name” are fairly simple since they require only direct mappings. So I’ll focus on the mapping field “PositionID” and “PositionName” as they are little more complex.
The field “PositionID” and “PositionName” use the same mapping pattern as below.
- Change the context of the source field “PositionID” from Position to Employee.
- collapseContext: To get the first value from each context. In our case, it’s a current employee’s position.
- splitByValue: It’s simply to insert context changes based on each value changes by default.
The output should look like this.
Source:
<Employees>
<Employee>
<PERNR>1000</PERNR>
<Name>Mr. A</Name>
<Position>
<PositionID>300</PositionID>
<PositionName>Position A</PositionName>
</Position>
<Position>
<PositionID>400</PositionID>
<PositionName>Position B</PositionName>
</Position>
</Employee>
<Employee>
<PERNR>2000</PERNR>
<Name>Mr. B</Name>
<Position>
<PositionID>500</PositionID>
<PositionName>Position C</PositionName>
</Position>
<Position>
<PositionID>600</PositionID>
<PositionName>Position D</PositionName>
</Position>
</Employee>
<Employee>
<PERNR>3000</PERNR>
<Name>Mr. C</Name>
<Position>
<PositionID>800</PositionID>
<PositionName>Position G</PositionName>
</Position>
</Employee>
</Employees>
Target:
<Employees>
<Employee>
<PERNR>1000</PERNR>
<Name>Mr. A</Name>
<PositionID>300</PositionID>
<PositionName>Position A</PositionName>
</Employee>
<Employee>
<PERNR>2000</PERNR>
<Name>Mr. B</Name>
<PositionID>500</PositionID>
<PositionName>Position C</PositionName>
</Employee>
<Employee>
<PERNR>3000</PERNR>
<Name>Mr. C</Name>
<PositionID>800</PositionID>
<PositionName>Position G</PositionName>
</Employee>
</Employees>
Convert a tree structure to a flat structure (Case 2)
Scenario: To convert a tree structure to a flat structure
Integration scenario: Replicate a header-level value to multiple detail nodes
Mapping functions: removeContexts, useOneAsMany
What we are trying to achieve is to transform a source (tree) structure to a target (flat) structure.
The expected output will be more or less like this one. It’s similar to the above case but instead, we take all positions, not only the first one.
The root “Employees” element uses a direct mapping.
As the “Employee” element should be repeated for each “Position” element, so let map these elements together. Then we should add the mapping function removeContext to get a total of “Position” element irrespective of context changes.
From the above example, there are 2 positions for each employee. Since there are 2 employees, so we will get a total of 4 positions after contexts removed.
Now let’s map the “PERNR” value. We want the source “PERNR” field value to be replicated as many times as the “Position” elements occur.
So we use the mapping function useOneAsMany to get the value from input 1 (“PERNR”) to be occurred as many times as input 2 (“Position”) and put the output in the structure input 3(“PositionID”).
The mapping of “Name” is similar to the above “PERNR” mapping. It just changes the source/target fields to “Name”.
For the “PositionID” and “Position” fields need only direct mappings.
Finished! Let’s see the outcome.
Source:
<Employees>
<Employee>
<PERNR>1000</PERNR>
<Name>Mr. A</Name>
<Position>
<PositionID>300</PositionID>
<PositionName>Position A</PositionName>
</Position>
<Position>
<PositionID>400</PositionID>
<PositionName>Position B</PositionName>
</Position>
</Employee>
<Employee>
<PERNR>2000</PERNR>
<Name>Mr. B</Name>
<Position>
<PositionID>500</PositionID>
<PositionName>Position C</PositionName>
</Position>
<Position>
<PositionID>600</PositionID>
<PositionName>Position D</PositionName>
</Position>
</Employee>
</Employees>
Target:
<Employees>
<Employee>
<PERNR>1000</PERNR>
<Name>Mr. A</Name>
<PositionID>300</PositionID>
<PositionName>Position A</PositionName>
</Employee>
<Employee>
<PERNR>1000</PERNR>
<Name>Mr. A</Name>
<PositionID>400</PositionID>
<PositionName>Position B</PositionName>
</Employee>
<Employee>
<PERNR>2000</PERNR>
<Name>Mr. B</Name>
<PositionID>500</PositionID>
<PositionName>Position B</PositionName>
</Employee>
<Employee>
<PERNR>2000</PERNR>
<Name>Mr. B</Name>
<PositionID>600</PositionID>
<PositionName>Position B</PositionName>
</Employee>
</Employees>
I hope that this blog is helpful in one way or another.
Hi Eaksiri
Very thorough write-up! Your explanation of context is very good. In future, I'll refer people here, when they're confused by context.
Thank you,
Morten
Excellent Blog !!
good guide for people that are begining!
Excellent blog for beginners and intermediate alike!
Nice Info regarding Mapping ...
Nicely explained. Kudos to make such effort.
Very helpful blog !!! Thanks.
I have a question on the chapter "Convert a flat structure to a tree structure".
How do you make the mapping for field Manager_Name ? It is not a direct mapping, isn't it ?
Thanks.
Very helpful blog.Thanks mate.
Very helpful blog.Thanks mate.
wonderful work , it helped me understand the context . keep writing such informative blogs.
Nice Blog
nice blog
Very nice blog. I like the clear explanation of the context related topics
Very well explained along with examples in a layman terms. Kudos!
Can You Explain Content modifier in Message Header & Exchange Property. also explain i'm not under stand Header & Property same function in both.