0

I have a groovy code which accepts xpath expression and find the value of nodes (both XML and JSON), but I am not able to filter by attributes. Below is my code, please can I get some help?

def message = '''
<payload>
    <division name="DivisionCodes">
        <param name="DivisionCode">FFM-VKM</string>
        <param name="DivisionGroupCode">FFM</string>
        <param name="DivisionCountryCode">DE</string>
    </division>
</payload>
'''
def xpathEx='*/param[@name="DivisionCountryCode"]'
def result = ''
if(xpathEx) {
    def gPaths = xpathEx?.split(",") as LinkedList
    def extractedData=[:]
    gPaths.eachWithIndex { String entry, int idx ->
        def inputData = ''
        def headerKey = ''
        def path = entry?.replace('/', '.')?.replace(':','')?.split('\\.') as LinkedList
        if (path) {
            if (message.trim().charAt(0) == '<') {
                inputData = new XmlSlurper().parseText(message)
            }
            path.forEach({ inputData = inputData."${it}" })
        }
        extractedData.put(path.getLast().toString(),inputData)
    }
    if(extractedData.isEmpty()) {
        result = ' '
    } else {
        result = ', XPath reference: '+extractedData?.toMapString()
    }                   
}
println result

I am interested to get the value of param child node where attribute is "DivisionGroupCode". My xpath expression is only able to filter by child node.

What should be the correct xpathEx? OR What should correct groovy code which can take xpathEx as parameter and process on any XML payload

Sambit Swain
  • 131
  • 1
  • 13
  • Can you do something like this https://stackoverflow.com/a/2269464/3166095 and use the XPath directly? – thehole Aug 15 '20 at 02:14
  • Strictly speaking you should write `//param[@name="DivisionCountryCode"]`. And if your XPath processor supports it `//param[@name="DivisionCountryCode"]/text()`. Be sure also to check if there's no namespaces in your XML document. – E.Wiest Aug 15 '20 at 02:18
  • I tried both solutions and none of them worked. I used the same above code for execution. @thehole if I change the code as suggested in the link provided then xpath would be needing arg change – Sambit Swain Aug 15 '20 at 07:12
  • I believe using XmlSlurper we cannot filter an attribute or node by its value. For example: I cannot get the value of the child node if it contains an attribute "name" with value as "Gym". How can I get my result as Shutdown? Overflow Shutdown Overflow – Sambit Swain Aug 15 '20 at 08:28
  • Xmlslurper doesn't support xpath. Better to use dom+xpath or xmlslurper+gpath. – daggett Aug 15 '20 at 12:05
  • I am using xmlslurper+gpath, and I got the expression to find the value of attribute. But cannot get an expression which will give me node value if the attribute value is ABCD. Could you please advise? – Sambit Swain Aug 15 '20 at 14:20

2 Answers2

0

If yourt only requirement is like you stated: "I am interested to get the value of param child node where attribute is 'DivisionGroupCode'", then this will work. However, your XML is not valid, the closing tags: </string> should be </param>.

def message = '''
<payload>
    <division name="DivisionCodes">
        <param name="DivisionCode">FFM-VKM</param>
        <param name="DivisionGroupCode">FFM</param>
        <param name="DivisionCountryCode">DE</param>
    </division>
</payload>
'''

def payload = new XmlSlurper().parseText(message)

def name = payload.depthFirst().find { param ->
    param.@name == 'DivisionGroupCode'
}

println name
ou_ryperd
  • 2,037
  • 2
  • 18
  • 23
  • Hello, thank you for the reply. It works for me, but aren't we forcefully declaring the attribute name in "param.@name == 'DivisionGroupCode'"?? If for another XML my attribute name is "attrVal", then the above code won't work. I tried to pass the attribute name as argument like "def att="@name" param.att=='DivisionGroupCode'". This way, it doesn't work – Sambit Swain Aug 16 '20 at 09:01
  • I have finally figured out the solution, it's now accepting all arguments and filters are parametrised. – Sambit Swain Aug 16 '20 at 10:09
0
import groovy.json.JsonSlurper

def result = ''
def message = '''
<payload>
    <division name="DivisionCodes">
        <param name="DivisionCode">FFM-VKM</param>
        <param name="DivisionGroupCode">FFM</param>
        <param name="DivisionCountryCode">DE</param>
    </division>
    <division value="SectorCodes">
        <param value="SectorCode">SEC-LOC-23</param>
        <param value="SectorGroupCode">DUK-001002</param>
        <param value="SectorCountryCode">PH</param>
    </division>
</payload>
'''
def xpathEx='*@name="DivisionCode",*@value="SectorCode",*@name="DivisionCountryCode",*@value="SectorCountryCode"'

if(xpathEx) {
    def gPaths = xpathEx?.split(",") as LinkedList
    def extractedData=[:]
    gPaths.eachWithIndex { String entry, int idx ->
        def inputData = ''
        def path = entry?.replace('/', '.')?.replace('@', '.@')?.replace(':','')?.split('\\.') as LinkedList
        if (path) {
            if (message.trim().charAt(0) == '<') {
                inputData = new XmlSlurper().parseText(message)
            } else if (message.trim().charAt(0) == '{') {
                inputData = new JsonSlurper().parseText(message)
            }
            path.forEach({ 
                if ("${it}".contains("=")) {
                    def attName = "${it}".substring(1, "${it}".indexOf("="))
                    def attValue = "${it}".substring("${it}".indexOf("=") + 2, "${it}".length() - 1)
                    inputData = inputData.depthFirst().find {arg -> if (arg.attributes().get(attName) == attValue) { arg } }
                } else {
                    inputData = inputData."${it}" 
                }
            })
        }
        extractedData.put(path.getLast().toString(),inputData)
    }
    if(extractedData.isEmpty()) {
        result = ' '
    } else {
        result = ', XPath reference: '+extractedData?.toMapString()
    }                   
}
println result
Sambit Swain
  • 131
  • 1
  • 13