cancel
Showing results for 
Search instead for 
Did you mean: 
Read only

Groovy or XSLT to remove XML Tag(not values) in sap cpi payload

nidhi_srivastava22
Active Contributor
0 Kudos
1,384

Hi Experts,

I have a XML payload from which I need to remove only the XML tag and the values should pass to the parent tag. Sample payload mentioned below -

nidhi_srivastava22_0-1727005601585.png

I tried few groovy functions mentioned on the community but that is replacing the entire <AdditionalComments/> tag to blank which is not expected.

Please help me if we can take this with groovy or I need to go with XSLT?

Thanks.

Best Regards,

Nidhi Srivastava

Accepted Solutions (1)

Accepted Solutions (1)

nageshrao
Product and Topic Expert
Product and Topic Expert
0 Kudos

Hello Nidhi, 

As Ryan mentioned, the provided sample XML payload is invalid. If the source XML contains unescaped special characters like '@', '<', or '>', XSLT processing will fail. A Groovy script is a better option for handling invalid XML and generating valid output. The script is more robust than XSLT as it can handle XML validity issues. However, it assumes a specific structure, so you may need to modify it based on changes in your payload. Use the script below as a reference starting point to build/ customise as per your requirement.

Groovy Script - 

import com.sap.gateway.ip.core.customdev.util.Message
import groovy.xml.*

// Function to process the incoming message
def Message processData(Message message) {
    // Get the body of the message as a String
    def body = message.getBody(String)
    def result

    try {
        // Parse the XML body using XmlParser
        def xmlParser = new XmlParser(false, false)
        def rootNode = xmlParser.parseText(body)

        // Prepare to build the new XML output
        def writer = new StringWriter()
        def xmlBuilder = new MarkupBuilder(writer)

        // Create the Root element
        xmlBuilder.Root {
            // Traverse all nodes in the XML
            rootNode.depthFirst().each { node ->
                // Check for the Root1 element
                if (node.name() == 'Root1') {
                    Root1 {
                        // Process each child node of Root1
                        node.children().each { childNode ->
                            // If the child is AssignedTo, extract the email
                            if (childNode.name() == 'AssignedTo') {
                                def emailMatch = (childNode.text() =~ /([^<\s]+@[^>]+)/)
                                if (emailMatch) {
                                    // Add only the email to the output
                                    AssignedTo(emailMatch[0][0])
                                }
                            } else if (childNode.name() == 'AdditionalComments') {
                                // Extract comments from the div tag
                                AdditionalComments(childNode.div.text())
                            } else {
                                // Add other child nodes as they are
                                "${childNode.name()}"(childNode.text())
                            }
                        }
                    }
                }
            }
        }

        // Convert the writer output to string
        result = writer.toString()
    } catch (Exception e) {
        // If parsing fails, handle it with a more lenient approach
        def lines = body.readLines()
        // Initialize the output with the XML declaration and Root tag
        def output = new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Root>\n")

        boolean inRoot1 = false
        // Process each line of the original XML
        lines.each { line ->
            // Skip XML declaration and Root tags
            if (line.contains('<?xml') || line.contains('<Root>') || line.contains('</Root>')) {
                return
            }
            // Detect the start of Root1 and set the flag
            if (line.contains('<Root1>')) {
                inRoot1 = true
                output << "  <Root1>\n"
                return
            }
            // Detect the end of Root1 and reset the flag
            if (line.contains('</Root1>')) {
                inRoot1 = false
                output << "  </Root1>\n"
                return
            }
            // If we are inside Root1, process its children
            if (inRoot1) {
                if (line.contains('<AssignedTo>')) {
                    // Extract email from AssignedTo using regex
                    def emailMatch = (line =~ /([^<\s]+@[^>]+)/)
                    if (emailMatch) {
                        output << "    <AssignedTo>${emailMatch[0][0]}</AssignedTo>\n"
                    }
                } else if (line.contains('<AdditionalComments>')) {
                    // Extract comments from the div tag
                    def comment = line.findAll(/<div>(.*?)<\/div>/)
                    if (comment) {
                        output << "    <AdditionalComments>${comment[0][1]}</AdditionalComments>\n"
                    }
                } else if (line.trim().startsWith('<') && line.trim().endsWith('>')) {
                    // Add other tags as they are
                    output << "    ${line.trim()}\n"
                }
            }
        }

        // Close the Root tag
        output << '</Root>'
        result = output.toString()
    }

    // Set the transformed XML as the body of the message
    message.setBody(result)
    return message
}


Source Invalid XML - 

<?xml version='1.0' encoding='UTF-8'?>
<Root>
<Root1>
<ADONumber>12345</ADONumber>
<State></State>
<AssignedTo>SRIVASTAVA Nidhi (External) <nidhi.srivastava.ext@XXXXX.eu> </AssignedTo>
<Priority></Priority>
<AdditionalComments>
<div>Comment 5</div>
</AdditionalComments>
</Root1>
</Root>

Output Valid XML - 

<?xml version="1.0" encoding="UTF-8"?>
<Root>
<Root1>
<ADONumber>12345</ADONumber>
<State></State>
<AssignedTo>nidhi.srivastava.ext@XXXXX.eu</AssignedTo>
<Priority></Priority>
<AdditionalComments>d</AdditionalComments>
</Root1>
</Root>



nidhi_srivastava22
Active Contributor
0 Kudos
Thanks a lot Nagesh for your help. Marking the solution as accepted.
nidhi_srivastava22
Active Contributor
0 Kudos

Hi Nagesh @nageshrao  ,

I have another issue related to this blog which I posted in another thread as this thread was already closed. If you can suggest, it will be of great help. 

https://community.sap.com/t5/technology-q-a/structure-formatting-for-receiver-in-sap-cpi-through-gro... 

Thanks.

Best Regards,

Nidhi Srivastava

Answers (1)

Answers (1)

nageshrao
Product and Topic Expert
Product and Topic Expert

Hi Nidhi, 

Below XSLT will achieve the result you want

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    
    <!-- Identity transform using xsl:mode -->
    <xsl:mode on-no-match="shallow-copy"/>
    
    <!-- Special handling for AdditionalComments -->
    <xsl:template match="AdditionalComments">
        <xsl:copy>
            <xsl:value-of select="div"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

 

nidhi_srivastava22
Active Contributor
0 Kudos

Hi Nagesh,

Thanks for your help. This code works fine, but it fails when there is assigned to field sent.

<AssignedTo>ABC XYZ <ABC.XYZ@gmail.com></AssignedTo>

 

 
Error Details
net.sf.saxon.trans.XPathException: Error reported by XML parser: Unexpected character '@' (code 64) expected space, or '>' or "/>" at [row,col {unknown-source}]: [6,49}
 
Can we format the XSLT like to check only in AdditionalComments field if that is present or ignore the AssignedTo field for the check?
 
Thanks,
Nidhi Srivastava
nageshrao
Product and Topic Expert
Product and Topic Expert
0 Kudos

Hello Nidhi,
The error occurs because the email address in the AssignedTo field contains the '@' character, which is not valid in XML content outside of attributes.
Assuming you will get this '@' only in the field - "AssignedTo" we can update the XSLT to ignore that as below 

 

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    
    <!-- Identity transform using xsl:mode -->
    <xsl:mode on-no-match="shallow-copy"/>
    
    <!-- Special handling for AdditionalComments -->
    <xsl:template match="AdditionalComments">
        <xsl:copy>
            <xsl:value-of select="div"/>
        </xsl:copy>
    </xsl:template>

    <!-- Explicitly copy AssignedTo without changes -->
    <xsl:template match="AssignedTo">
        <xsl:copy-of select="."/>
    </xsl:template>

</xsl:stylesheet>

 




nidhi_srivastava22
Active Contributor
0 Kudos

Hi Nagesh,

I tried this but still it is not working. I also tried to keep the explicit copy in order but still it is showing the same error.

nidhi_srivastava22_0-1727173644246.png

Thanks for your advise.

Best Regards,

Nidhi Srivastava

nageshrao
Product and Topic Expert
Product and Topic Expert
0 Kudos
Hi Nidhi,Can you please paste the complete sample source XML ?
nidhi_srivastava22
Active Contributor
0 Kudos
Hi Nagesh,
 
PFB the source sample payload.
 
<?xml version='1.0' encoding='UTF-8'?>
<Root1>
    <Root2>
        <ADONumber>12345</ADONumber>
        <State></State>
        <AssignedTo>SRIVASTAVA Nidhi (External) <nidhi.srivastava.ext@XXXXX.eu> </AssignedTo>
            <Priority></Priority>
            <AdditionalComments></AdditionalComments>
        </Root2>
    </Root1>
 
Just discussed on the issue & was suggested with a workaround that instead of sending the full details I can only send the email id. 
<AssignedTo>nidhi.srivastava.ext@XXXXX.eu</AssignedTo> this can be added in the XSLT or can i add a groovy step before to remove the same.
 
Thanks a lot for your advice.
 
Regards,
Nidhi
Ryan-Crosby
Active Contributor
0 Kudos
@nidhi_srivastava22 That is not valid XML.