In SAP Cloud Platform Integration (CPI), efficiently merging XML documents is crucial for integrating diverse systems. This blog post explores how to enhance your integration scenarios by employing Groovy scripts for XML merging in SAP CPI. XML merging becomes essential when you need to pull and merge data from multiple sources.
In this scenario we have two xml from different sources. Where we couldn’t use enrich in the flow to merge them. We are going to merge them according to their emails.
Prerequisites
Before putting the code in flow, you need to have these properties:
Name of property Description In our scenario
firstBodyData first xml body
secBodyData second xml body
firstArea fields to be matched on in first xml root/value/mail
secArea fields to be matched on in second xml root/Resources/emails/value
firstStruct structure to be matched from first xml root/value
secStruct structure to be matched from second xml root/Resources
Another thing to be aware of is this code can only work when data are under one level in the structure. For example:
<root>
<value>
…
</value>
<value>
…
</value>
<value>
…
</value>
…
</root>
The Groovy Script
import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;
import groovy.xml.*
import groovy.xml.MarkupBuilder ;
def getNodeByPath(node, path) {
def pathElements = path.split(‘/’)
def current = node
pathElements.each { element ->
current = current.”$element”
}
return current
}
def Message processData(Message message) {
properties = message.getProperties();
firstBodyData = properties.get(“firstBodyData”) as String
secBodyData = properties.get(“secBodyData”) as String
firstArea = properties.get(“firstArea”);
secArea = properties.get(“secArea”);
firstStruct = properties.get(“firstStruct”);
secStruct = properties.get(“secStruct”);
def firstBody = new XmlParser().parseText(firstBodyData)
def secBody = new XmlParser().parseText(secBodyData)
firstArea1 = firstArea.split(‘^[A-z0-9]*/’)
firstAfterRoot = firstArea1[1].find(‘^[A-z0-9]*’)
firstAreaF = firstArea1[1].split(‘^[A-z0-9]*/’)[1]
//get list of firstarea = arealist
def areaList = firstBody.”${firstAfterRoot}”.collect { getNodeByPath(it,firstAreaF).text() }
areaList = areaList.collect{ it.toString().toLowerCase() }
secArea1 = secArea.split(‘^[A-z0-9]*/’)
secAfterRoot = secArea1[1].find(‘^[A-z0-9]*’)
secAreaF = secArea1[1].split(‘^[A-z0-9]*/’)[1]
def secAreaList = secBody.”${secAfterRoot}”.collect { getNodeByPath(it,secAreaF).text() }
secAreaList = secAreaList.collect{ it.toString().toLowerCase() }
//get list of numbers of area in the sec body = secareanum
def secareanum = []
for(int i = 0; i < areaList.size(); i++){
secAreaList.eachWithIndex{it,index->
if(it.equals(areaList[i])){
secareanum[i] = index
}
}
}
//xml according to structs
def firstStructf = firstStruct.split(‘^[A-z0-9]*/’)
def secStructf = secStruct.split(‘^[A-z0-9]*/’)
def firstNode = getNodeByPath(firstBody, firstStructf[1])
def secNode = getNodeByPath(secBody, secStructf[1])
def tmpNode
def inode
def varStringWriter = new StringWriter();
def indentPrinter = new IndentPrinter(varStringWriter, ‘ ‘)
def varXMLBuilder = new MarkupBuilder(indentPrinter);
varXMLBuilder.Root {
for(int i = 0; i < firstNode.size(); i++){
‘Entry’ {
‘id’ areaList[i]
}
}
}
def newnode = new XmlParser().parseText(varStringWriter.toString())
newnode.eachWithIndex{it,index->
if(firstNode[index]==null){
tmpNode = null
}else{
tmpNode = firstNode[index].children()
}
if(secareanum[index]==null){
tmpNode2 = null
}else{
tmpNode2 = secNode[secareanum[index]].children()
}
it.appendNode(
firstNode[0].name(), tmpNode
)
it.appendNode(
secNode[0], tmpNode2
)
}
message.body = XmlUtil.serialize(newnode)
return message;
}
The first function getNodeByPath is used for getting the desired node that is given in the path.In the start of the main function, we are getting the properties to variables and parsing the XML’s.We create firstAfterRoot and firstAreaF variables to search the emails in the XML and put them into a list (areaList) then we make it lower case.We do the same thing for the second XML.We create a for loop to create a list (secareanum) for the places of emails in the second XML.
For example,
areaList = [1@email.com, 2@email.com, 3@email.com, , 4@email.com]
secAreaList = [5@email.com, 3@email.com, 2@email.com, 4@email.com, 1@email.com]
secareanum = [4, 2, 1, null,3]
We get the structures that are needed (firstNode, secNode).We define variables to build our new XML.Initialize the XML with matching element as id with for loop.Parse the new XML into newnode because we are going to append nodes.Using eachWithIndex to loop with nodes we are appending nodes and controlling the null values with temporary nodes.Lastly, we serialize the newnode and put it as message to return.
In our example first body is like this:
<root>
<value>
<mail>1@Email.com</mail>
</value>
<value>
<mail>2@email.com</mail>
</value>
<value>
<mail>3@email.com</mail>
</value>
<value>
<mail></mail>
</value>
<value>
<mail>4@email.com</mail>
</value>
</root>
Second structure:
<root>
<Resources>
<name>firstname5</name>
<emails>
<value>5@email.com</value>
</emails>
</Resources>
<Resources>
<emails>
<value>3@email.com</value>
</emails>
</Resources>
<Resources>
<name>firstname2</name>
<emails>
<value>2@email.com</value>
</emails>
</Resources>
<Resources>
<emails>
<value>4@email.com</value>
</emails>
</Resources>
<Resources>
<name>firstname</name>
<emails>
<value>1@email.com</value>
</emails>
</Resources>
</root>
Result’s structure is like this:
<?xml version=”1.0″ encoding=”UTF-8″?>
<Root>
<Entry>
<id>1@email.com</id>
<value>
<mail>1@Email.com</mail>
</value>
<Resources>
<name>firstname</name>
<emails>
<value>1@email.com</value>
</emails>
</Resources>
</Entry>
<Entry>
<id>2@email.com</id>
<value>
<mail>2@email.com</mail>
</value>
<Resources>
<name>firstname2</name>
<emails>
<value>2@email.com</value>
</emails>
</Resources>
</Entry>
<Entry>
<id>3@email.com</id>
<value>
<mail>3@email.com</mail>
</value>
<Resources>
<emails>
<value>3@email.com</value>
</emails>
</Resources>
</Entry>
<Entry>
<id/>
<value>
<mail/>
</value>
<Resources/>
</Entry>
<Entry>
<id>4@email.com</id>
<value>
<mail>4@email.com</mail>
</value>
<Resources>
<emails>
<value>4@email.com</value>
</emails>
</Resources>
</Entry>
</Root>
Conclusion:
By integrating Groovy scripts into SAP CPI, you can seamlessly merge XML documents, without using complex mappings or XSLT codes on the flow.
I hope that this blog will help you to merge your documents!
Thank you so much for reading my first blog!
In SAP Cloud Platform Integration (CPI), efficiently merging XML documents is crucial for integrating diverse systems. This blog post explores how to enhance your integration scenarios by employing Groovy scripts for XML merging in SAP CPI. XML merging becomes essential when you need to pull and merge data from multiple sources.In this scenario we have two xml from different sources. Where we couldn’t use enrich in the flow to merge them. We are going to merge them according to their emails.PrerequisitesBefore putting the code in flow, you need to have these properties:Name of property Description In our scenariofirstBodyData first xml bodysecBodyData second xml bodyfirstArea fields to be matched on in first xml root/value/mailsecArea fields to be matched on in second xml root/Resources/emails/valuefirstStruct structure to be matched from first xml root/valuesecStruct structure to be matched from second xml root/ResourcesAnother thing to be aware of is this code can only work when data are under one level in the structure. For example: <root>
<value>
…
</value>
<value>
…
</value>
<value>
…
</value>
…
</root> The Groovy Script import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;
import groovy.xml.*
import groovy.xml.MarkupBuilder ;
def getNodeByPath(node, path) {
def pathElements = path.split(‘/’)
def current = node
pathElements.each { element ->
current = current.”$element”
}
return current
}
def Message processData(Message message) {
properties = message.getProperties();
firstBodyData = properties.get(“firstBodyData”) as String
secBodyData = properties.get(“secBodyData”) as String
firstArea = properties.get(“firstArea”);
secArea = properties.get(“secArea”);
firstStruct = properties.get(“firstStruct”);
secStruct = properties.get(“secStruct”);
def firstBody = new XmlParser().parseText(firstBodyData)
def secBody = new XmlParser().parseText(secBodyData)
firstArea1 = firstArea.split(‘^[A-z0-9]*/’)
firstAfterRoot = firstArea1[1].find(‘^[A-z0-9]*’)
firstAreaF = firstArea1[1].split(‘^[A-z0-9]*/’)[1]
//get list of firstarea = arealist
def areaList = firstBody.”${firstAfterRoot}”.collect { getNodeByPath(it,firstAreaF).text() }
areaList = areaList.collect{ it.toString().toLowerCase() }
secArea1 = secArea.split(‘^[A-z0-9]*/’)
secAfterRoot = secArea1[1].find(‘^[A-z0-9]*’)
secAreaF = secArea1[1].split(‘^[A-z0-9]*/’)[1]
def secAreaList = secBody.”${secAfterRoot}”.collect { getNodeByPath(it,secAreaF).text() }
secAreaList = secAreaList.collect{ it.toString().toLowerCase() }
//get list of numbers of area in the sec body = secareanum
def secareanum = []
for(int i = 0; i < areaList.size(); i++){
secAreaList.eachWithIndex{it,index->
if(it.equals(areaList[i])){
secareanum[i] = index
}
}
}
//xml according to structs
def firstStructf = firstStruct.split(‘^[A-z0-9]*/’)
def secStructf = secStruct.split(‘^[A-z0-9]*/’)
def firstNode = getNodeByPath(firstBody, firstStructf[1])
def secNode = getNodeByPath(secBody, secStructf[1])
def tmpNode
def inode
def varStringWriter = new StringWriter();
def indentPrinter = new IndentPrinter(varStringWriter, ‘ ‘)
def varXMLBuilder = new MarkupBuilder(indentPrinter);
varXMLBuilder.Root {
for(int i = 0; i < firstNode.size(); i++){
‘Entry’ {
‘id’ areaList[i]
}
}
}
def newnode = new XmlParser().parseText(varStringWriter.toString())
newnode.eachWithIndex{it,index->
if(firstNode[index]==null){
tmpNode = null
}else{
tmpNode = firstNode[index].children()
}
if(secareanum[index]==null){
tmpNode2 = null
}else{
tmpNode2 = secNode[secareanum[index]].children()
}
it.appendNode(
firstNode[0].name(), tmpNode
)
it.appendNode(
secNode[0], tmpNode2
)
}
message.body = XmlUtil.serialize(newnode)
return message;
} The first function getNodeByPath is used for getting the desired node that is given in the path.In the start of the main function, we are getting the properties to variables and parsing the XML’s.We create firstAfterRoot and firstAreaF variables to search the emails in the XML and put them into a list (areaList) then we make it lower case.We do the same thing for the second XML.We create a for loop to create a list (secareanum) for the places of emails in the second XML.For example,areaList = [1@email.com, 2@email.com, 3@email.com, , 4@email.com]secAreaList = [5@email.com, 3@email.com, 2@email.com, 4@email.com, 1@email.com]secareanum = [4, 2, 1, null,3]We get the structures that are needed (firstNode, secNode).We define variables to build our new XML.Initialize the XML with matching element as id with for loop.Parse the new XML into newnode because we are going to append nodes.Using eachWithIndex to loop with nodes we are appending nodes and controlling the null values with temporary nodes.Lastly, we serialize the newnode and put it as message to return.In our example first body is like this:<root>
<value>
<mail>1@Email.com</mail>
</value>
<value>
<mail>2@email.com</mail>
</value>
<value>
<mail>3@email.com</mail>
</value>
<value>
<mail></mail>
</value>
<value>
<mail>4@email.com</mail>
</value>
</root>Second structure:<root>
<Resources>
<name>firstname5</name>
<emails>
<value>5@email.com</value>
</emails>
</Resources>
<Resources>
<emails>
<value>3@email.com</value>
</emails>
</Resources>
<Resources>
<name>firstname2</name>
<emails>
<value>2@email.com</value>
</emails>
</Resources>
<Resources>
<emails>
<value>4@email.com</value>
</emails>
</Resources>
<Resources>
<name>firstname</name>
<emails>
<value>1@email.com</value>
</emails>
</Resources>
</root>Result’s structure is like this: <?xml version=”1.0″ encoding=”UTF-8″?>
<Root>
<Entry>
<id>1@email.com</id>
<value>
<mail>1@Email.com</mail>
</value>
<Resources>
<name>firstname</name>
<emails>
<value>1@email.com</value>
</emails>
</Resources>
</Entry>
<Entry>
<id>2@email.com</id>
<value>
<mail>2@email.com</mail>
</value>
<Resources>
<name>firstname2</name>
<emails>
<value>2@email.com</value>
</emails>
</Resources>
</Entry>
<Entry>
<id>3@email.com</id>
<value>
<mail>3@email.com</mail>
</value>
<Resources>
<emails>
<value>3@email.com</value>
</emails>
</Resources>
</Entry>
<Entry>
<id/>
<value>
<mail/>
</value>
<Resources/>
</Entry>
<Entry>
<id>4@email.com</id>
<value>
<mail>4@email.com</mail>
</value>
<Resources>
<emails>
<value>4@email.com</value>
</emails>
</Resources>
</Entry>
</Root> Conclusion:By integrating Groovy scripts into SAP CPI, you can seamlessly merge XML documents, without using complex mappings or XSLT codes on the flow.I hope that this blog will help you to merge your documents!Thank you so much for reading my first blog! Read More Technology Blogs by Members articles
#SAP
#SAPTechnologyblog