-->

How to I use a variable reference within XMLSlurpe

2019-03-06 10:09发布

问题:

I am using a groovy with XMLSlurper to validate my web service responses in soap ui pro.

I have the following code which works (expectedResponse is var that stores expected errorcode e.g. E0023) ...

if(expectedResponse2 in slurper.Body.createShipmentResponse.integrationFooter.errors.error.errorCode.collect{it.text()})
{
        result = "pass" 
}

But I would like to replace the 'integrationFooter.errors.error.errorCode' with a reference to a variable that I could supply from a SoapUI Pro datasource, because I am not always validating on the same response element. i.e if I am expecting the test to pass I might want to check that the status element is populated with 'Allocated'. If I am expecting the request to fail I want to validate that the errorCode field is populated with the correct errorCode e.g. 'E0023'.

If I have a variable called testElement in my groovyscript and I assign it the path of the element e.g. integrationFooter.errors.error.errorCode how do I refer to the variable within my XMLSlurper statement?

I tried the below code but it didn't work..

if(expectedResponse2 in slurper.Body.createShipmentResponse."${testElement}".collect{it.text()})

回答1:

You need to split the string and handle each property in turn yourself...

Try:

def root = slurper.Body.createShipmentResponse
if(expectedResponse2 in testElement.tokenize( '.' ).inject( root ) { v, p ->
                            v?."$p"
                        }?.collect{ it.text() } ) {
    result = "pass" 
}

See Access object properties in groovy using []

That loops over the list [ 'integrationFooter', 'errors', 'error', 'errorCode' ], starting with slurper.Body.createShipmentResponse. So first time round, the inject returns slurper.Body.createShipmentResponse.integrationFooter. Then it calls errors on this object, and so on until it gets the final result which you can call collect on.

The ?. operator just means it will keep going if it hits a null (and return null)

Inject explanation:

Given an object like this:

def obj = [ prop1:[ prop2:[ prop3: 'tim' ] ] ]

We can call:

assert obj.prop1.prop2.prop3 == 'tim'

However, if we were given the property name to read as a String:

def props = 'prop1.prop2.prop3'

Then this won't work (as there's no key 'prop1.prop2.prop3')

assert obj."$props" == 'tim'  // FAILS!

But we can split props on the full stop to get a list of properties:

def pList = props.tokenize( '.' )
assert pList == [ 'prop1', 'prop2', 'prop3' ]

Then, we can work through this list, starting with obj:

def result = pList.inject( obj ) { currentObject, property ->
    // Return the property from the currentObject (or null if currentObject is null)
    currentObject?."$property"
}

So for each element of pList, we do:

Step1:
    currentObject == obj
    property   == 'prop1'
    returns obj.prop1 -- [ prop2:[ prop3: 'tim' ] ]

Step2:
    currentObject == [ prop2:[ prop3: 'tim' ] ]
    property   == 'prop2'
    returns currentObj.prop2 -- [ prop3: 'tim' ]

Step3 (final step as pList is at an end now):
    currentObject == [ prop3: 'tim' ]
    property   == 'prop3'
    returns currentObj.prop3 -- 'tim'

So the final result is 'tim'

Hope this explains it :-)