Introduction:
In SAP Cloud Integration (CI and formerly known as CPI), Groovy scripts play a crucial role in customizing and fine-tuning integration flows. Whether you're manipulating XML structures, handling data transformations, or performing complex validations, Groovy scripts provide the flexibility you need. In this blog post, I’ll share several essential Groovy script solutions that address common challenges in SAP CI. These scripts, which you can integrate into your flows, include logic for sorting XML segments, managing data formats, and more.
When working with IDocs in SAP CI, the order of segments can be critical. This first script ensures that specific segments within an IDoc are sorted correctly according to a predefined order.
import com.sap.gateway.ip.core.customdev.util.Message
import groovy.xml.XmlUtil
import groovy.xml.StreamingMarkupBuilder
def Message processData(Message message) {
def body = message.getBody(java.lang.String) as String
def xml = new XmlSlurper().parseText(body)
// Define the desired order of segments
def desiredOrder = [
'EDI_DC40',
'ZAP2PO_MESSCONTRO',
'ZAP2PO_OPTIONS',
'ZAP2_POHEADER',
'ZAP2_POITEM',
'ZAP2_POACCOUNT',
'ZAP2_POACCOUNTPROFITSEGMENT',
'ZAP2_POSCHEDULE'
]
// Ensure xml.IDOC is a Node and get its children
def idocNode = xml.IDOC[0]
def sortedChildren = idocNode.children().sort { a, b ->
desiredOrder.indexOf(a.name()) <=> desiredOrder.indexOf(b.name())
}
// Rebuild the IDOC element with sorted children
def builder = new StreamingMarkupBuilder()
def sortedXml = builder.bind {
ZAP2POCREATE {
IDOC(BEGIN: '1') {
sortedChildren.each { child ->
mkp.yield child
}
}
}
}.toString()
// Set the sorted XML back to the message body
message.setBody(sortedXml)
return message
}
Explanation:
This script parses an incoming XML, sorts the segments within an IDoc according to a predefined order, and reconstructs the XML to ensure that downstream systems receive it in the expected format.
Sometimes, you may need to replace the root node of an XML document to conform to a new schema or to meet specific integration requirements. The following Groovy script shows how to replace the root node of an XML payload in SAP CI.
import com.sap.gateway.ip.core.customdev.util.Message
import groovy.xml.StreamingMarkupBuilder
import groovy.xml.XmlUtil
def Message processData(Message message) {
def body = message.getBody(java.lang.String) as String
def xml = new XmlSlurper().parseText(body)
// New root node name
def newRootNodeName = 'NewRootNode'
// Build the new XML structure with the new root node
def builder = new StreamingMarkupBuilder()
def newXml = builder.bind {
"${newRootNodeName}" {
xml.children().each { child ->
mkp.yield child
}
}
}.toString()
// Set the new XML back to the message body
message.setBody(newXml)
return message
}
#1 Test Execution result
#contiva IDE tool
If you need to change the root element from <ZAP2POCREATE> to <NewRootNode>, this script makes it easy and efficient to handle such transformations within your SAP CI flows.
When working with XML documents in Groovy, you might need to remove the XML declaration, especially if you're processing the XML further or integrating it with other systems. The XML declaration can vary in format, including different cases for encoding, the use of single or double quotes, or even omitting the encoding attribute altogether.
XML declaration types:
<?xml version="1.0" encoding="UTF-8"?>
<?xml version='1.0' encoding='UTF-8'?>
<?xml version="1.0" encoding="utf-8"?>
<?xml version='1.0' encoding='utf-8'?>
<?xml version="1.0"?>
<?xml version='1.0'?>
Here’s a Groovy script that handles all these variations:
import com.sap.gateway.ip.core.customdev.util.Message;
import groovy.xml.XmlUtil;
import groovy.xml.MarkupBuilder;
import java.util.HashMap;
import java.util.Map;
import groovy.xml.StreamingMarkupBuilder;
def processData(Message message) {
def body = message.getBody(java.lang.String) as String
// Regular expression to match all variations of the XML declaration
//Use (?i) within the regular expression to make it case-insensitive
def xmlDeclarationPattern = /(?i)<\?xml.*version\s*=\s*['"]1\.0['"](.*encoding\s*=\s*['"](utf-8)['"])?\s*\?>/
// Remove the XML declaration
body = body.replaceAll(xmlDeclarationPattern, "")
message.setBody(body) // Set the modified XML back to the message body
return message
}
Using groovy to transform XML input into a custom XML output.
import com.sap.gateway.ip.core.customdev.util.Message
import groovy.xml.MarkupBuilder
def Message processData(Message message) {
// Get the XML input as a string
def body = message.getBody(String)
def inputXml = new XmlSlurper().parseText(body)
// Initialize StringWriter to hold the new XML content
def writer = new StringWriter()
def xmlBuilder = new MarkupBuilder(writer)
// Handle single records ( 'Records' elements)
xmlBuilder.root {
User {
firstName(inputXml.Records.name1.text())
lastName(inputXml.Records.name2.text())
email(inputXml.Records.mailaccount.text())
phone(inputXml.Records.mailaccount.text())
recordtype("User Account")
}
}
//Handle unbounded records (multiple 'Records' elements)
/** xmlBuilder.root {
inputXml.Records.each{ recData ->
User {
firstName(recData.name1.text())
lastName(recData.name2.text())
email(recData.mailaccount.text())
phone(recData.mailaccount.text())
recordtype("User Account")
}
}
} **/
// Set the generated XML as the message body
message.setBody(writer.toString())
return message
}
#1 Test Execution - when the input contains one record
#2 Test Execution - when the input contains multiple records
Using groovy to transform XML input into a custom JSON output.
import com.sap.gateway.ip.core.customdev.util.Message
import groovy.json.JsonSlurper
import groovy.json.JsonBuilder
def Message processData(Message message) {
// Get the JSON input as a string
def body = message.getBody(String)
def inputJson = new JsonSlurper().parseText(body)
// Initialize a map to build the new JSON content
//Handle single record
/* def jsonContent = [
record: [
firstName : inputJson.root.Records.name1, //if input json is an error, even if it has one record, then define like this "inputJson.root.Records[0].name1"
lastName : inputJson.root.Records.name2,
email : inputJson.root.Records.mailaccount,
phone : inputJson.root.Records.mailaccount,
recordtype : "User Account"
]
]*/
//Handle muliple records (multiple 'Records' elements)
// Create a list to hold the JSON records
def jsonRecords = []
// Iterate over each <Records> element and build JSON objects
inputJson.root.Records.each { record ->
def jsonRecord = [
firstName: record.name1,
lastName: record.name2,
email: record.mailaccount,
phone: record.contactnumber,
recordtype : "User Account"
]
jsonRecords << jsonRecord
}
def jsonBuilder = new JsonBuilder(jsonRecords)
// Set the generated JSON as the message body
message.setBody(jsonBuilder.toPrettyString())
return message
}
#1 Test Execution - when the input contains one record
#2 Test Execution - when the input contains multiple records
Scripts 4 and 5 can be interchanged or customized as needed.
Conclusion:
In this blog post, we’ve covered several essential Groovy script solutions that address common challenges in SAP CI.
Feel free to adapt these scripts to your specific integration needs, and don’t hesitate to reach out if you have any questions or suggestions for additional topics!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
10 | |
9 | |
8 | |
7 | |
6 | |
5 | |
4 | |
4 | |
4 | |
3 |