Lesson 3 of 6
Working with XML and JSON
8 min
Listen to this lesson
Before you parse anything in a script, ask whether you need to. SAP's guidelines prefer message mappings, XSLT and XPath in Content Modifiers for transformations, because they are easier to read and maintain. Parse in a script when you need a value the standard steps can't reach cleanly, or when you build a small structure that isn't worth a mapping.
Reading XML with XmlSlurper
Use XmlSlurper for read-only parsing and XmlParser when you need to change the document in place. Parse from a Reader, not from a String: SAP's guideline says parseText(String) allocates extra memory that doesn't scale. For Contoso's ORDERS05 file: def root = new XmlSlurper().parse(message.getBody(Reader))def po = root.IDOC.E1EDK01.BELNR.text()def lines = root.IDOC.E1EDP01.size()message.setProperty('poNumber', po)
Mind the Groovy version. In script step 1.x (Groovy 2.4) XmlSlurper and XmlParser come from groovy.util, which Groovy imports by default. In version 2.0 (Groovy 4) SAP's upgrade guideline flags import groovy.util.XmlSlurper as incompatible: write import groovy.xml.XmlSlurper instead. The Problems view offers the fix automatically.
| Task | Script 1.x (Groovy 2.4) | Script 2.0 (Groovy 4) |
|---|---|---|
| Read XML | XmlSlurper (groovy.util, default import) | import groovy.xml.XmlSlurper |
| Change XML in place | XmlParser (groovy.util) | import groovy.xml.XmlParser |
| Build XML | groovy.xml.MarkupBuilder | groovy.xml.MarkupBuilder |
| Read and write JSON | groovy.json.JsonSlurper, JsonOutput | Same package, groovy.json |
Reading and writing JSON
For JSON, groovy.json.JsonSlurper parses and groovy.json.JsonOutput writes: def order = new JsonSlurper().parse(message.getBody(Reader))message.setBody(JsonOutput.toJson([po: po, lines: lines]))
Skip JsonOutput.prettyPrint on production paths: SAP recommends compact documents, since indentation and line breaks only add size.
Building XML
For a small target structure, groovy.xml.MarkupBuilder writes XML into a StringWriter: def w = new StringWriter()new MarkupBuilder(w).Lines { root.IDOC.E1EDP01.each { Line(po: po, item: it.POSEX.text()) } }message.setBody(w.toString())
If you find yourself building large documents this way, the logic probably belongs in a mapping.
When a script must change an existing document rather than read it, parse with XmlParser, change the nodes, and serialize the result back into the body. Keep that for small, local corrections, such as fixing one partner-specific attribute. If the change touches many fields, it's a mapping.
When XPath is enough
If all the script does is read one value, a Content Modifier does it with less risk. Create an exchange property poNumber of type XPath with the expression /ORDERS05/IDOC/E1EDK01/BELNR and data type java.lang.String. SAP's memory guideline adds a detail worth knowing: prefer absolute XPath expressions, because relative ones such as //BELNR make the parser search the whole document. IDoc XML has no namespaces, which keeps these expressions simple; partner formats often do, and then the XPath needs namespace mapping.
XmlSlurper is namespace-aware by default. For a document with a default namespace, navigate with the element's local names as usual, or declare prefixes with declareNamespace if you need to tell elements apart. Test a namespaced partner file in the local harness from lesson 6 before you rely on it.
Memory and security rules
| Rule (from SAP's scripting and memory guidelines) | Why |
|---|---|
| Parse from a Reader or InputStream, not parseText | Avoids an extra in-memory copy of large payloads |
| Build large strings with StringBuilder, not += | Repeated concatenation creates many intermediate objects |
| Declare variables with def or a type, never as bare binding variables | Binding variables live until undeploy, are shared between executions and aren't thread-safe |
| Remove large variables you no longer need (binding.variables.remove 'name') | Script variables stay in memory until the integration process ends |
| Don't call TimeZone.setDefault or classes generated by Eval() | Changes the JVM default time zone; Eval classes are never unloaded |
| If you use DocumentBuilderFactory, switch off external entities | Any XML parser is exposed to XML External Entity (XXE) attacks |
Guidelines that keep scripts fast and safe under load.
What would you do?
Great Lakes Fleet Services moves to a JSON order API. A developer proposes a 300-line Groovy script that reads the JSON with JsonSlurper and builds the ORDERS05 IDoc XML with MarkupBuilder. What should Theo suggest?
- Prefer mappings, XSLT and XPath; parse in scripts only when they can't do the job cleanly.
- XmlSlurper for reading, XmlParser for in-place changes; parse from a Reader.
- Groovy 4 (script 2.0) needs groovy.xml.XmlSlurper and groovy.xml.XmlParser imports.
- Write compact JSON and XML, use StringBuilder, avoid binding variables and disable XXE.
Check your understanding · not graded
Lesson check
1. Which call does SAP recommend for parsing a large XML body with XmlSlurper?
2. A script migrated to version 2.0 imports groovy.util.XmlSlurper. What does the Upgrade Readiness Check say?
3. Why should scripts avoid bare binding variables such as <code>body = '123'</code>?
4. Which JSON practice does SAP recommend for large documents on production paths?