XML Slurper - empty string for attributes - groovy

When parsing an attribute, the slurper sets an empty string when an attribute is not found.
For e.g., car.setOwner(node.#owner.text());
In the above code, if the owner attribute is not found, then the slurper sets a blank string ("").
In my case, I'd rather leave it as null than setting an empty string.
Is it possible to configure the Slurper not to do this?

You could do
car.setOwner(node.#owner.text() ?: null)

If we distinguish between configure and using the Meta Object Protocol (MOP), then we can state that it is not possible to configure XmlSlurper as you describe, but it is possible to use the MOP.
For configure, note the following:
def node = new XmlSlurper().parseText('<car>No Owner</car>' )
def attr = node.#owner
assert groovy.util.slurpersupport.Attributes == attr.getClass()
If you look at the code for Attributes.text() (in this case, Groovy 2.2.2), it is clear that this cannot be configured to return null.
For MOP, we can capture the original Attributes.text() method and then override it:
import groovy.util.slurpersupport.*
def originalText = Attributes.metaClass.getMetaMethod("text")
Attributes.metaClass.text = { ->
def result = originalText.invoke(delegate)
if (result.isEmpty()) {
result = null
}
result
}
// no owner
def node = new XmlSlurper().parseText('<car>No Owner</car>')
def attr = node.#owner
assert null == attr.text()
// with owner
node = new XmlSlurper().parseText('<car owner="codetojoy"></car>')
attr = node.#owner
assert "codetojoy" == attr.text()

Related

How do I define an entire object and methods on single line

This throws an error (can't set on null object)
def currentBuild = [:].rawBuild.getCauses = { return 'hudson.model.Cause$UserIdCause#123abc' }
I need to do it on multiple lines like this
def currentBuild = [:]
currentBuild.rawBuild = [:]
currentBuild.rawBuild.getCauses = { return 'hudson.model.Cause$UserIdCause#123abc' }
Is there a terse way to define this object on a single line or statement? I don't understand why my single line attempt doesn't work.
Instead of chaining setters, I'd just use a map literal with the nested
values. E.g.
def currentBuild = [rawBuild: [getCauses: { return 'hudson.model.Cause$UserIdCause#123abc' }]]
println currentBuild.rawBuild.getCauses()
// → hudson.model.Cause$UserIdCause#123abc
If you have to go more imperative instead of declarative, have a look at
.get(key, fallback), .withDefault{ ... }, .tap{ ... }.
BTW: those are not objects but just maps.

How to get a value of a dynamic key in Groovy JSONSlurper?

The variable resp contains below JSON response -
{"name":"sample","address":{"country":"IN","state":"TN","city":"Chennai"}}
I have planned using param1 variable to get the required key from JSON response, but I'm unable to get my expected results.
I'm passing the param1 field like - address.state
def actValToGet(param1){
JsonSlurper slurper = new JsonSlurper();
def values = slurper.parseText(resp)
return values.param1 //values.address.state
}
I'm getting NULL value here -> values.param1
Can anyone please help me. I'm new to Groovy.
The map returned from the JsonSlurper is nested rather than than flat. In other words, it is a map of maps (exactly mirroring the Json text which was parsed). The keys in the first map are name and address. The value of name is a String; the value of address is another map, with three more keys.
In order to parse out the value of a nested key, you must iterate through each layer. Here is a procedural solution to show what's happening.
class Main {
static void main(String... args) {
def resp = '{"name":"sample","address":{"country":"IN","state":"TN","city":"Chennai"}}'
println actValToGet(resp, 'address.state')
}
static actValToGet(String resp, String params){
JsonSlurper slurper = new JsonSlurper()
def values = slurper.parseText(resp)
def keys = params.split(/\./)
def output = values
keys.each { output = output.get(it) }
return output
}
}
A more functional approach might replace the mutable output variable with the inject() method.
static actValToGet2(String resp, String params){
JsonSlurper slurper = new JsonSlurper()
def values = slurper.parseText(resp)
def keys = params.split(/\./)
return keys.inject(values) { map, key -> map.get(key) }
}
And just to prove how concise Groovy can be, we can do it all in one line.
static actValToGet3(String resp, String params){
params.split(/\./).inject(new JsonSlurper().parseText(resp)) { map, key -> map[key] }
}
You may want to set a debug point on the values output by the parseText() method to understand what it's returning.

Is there a Groovy equivalent to Gradle findProperty?

I'm looking for a way to avoid having to check for the property first with hasProperty().
Ideally I would like to have something like
def a = myobj.getPropertyOrElse("mypropname", "defaultvalueifpropertymissing')
I see that gradle has a findProperty() but I can't find a similar thing for plain groovy.
The hasProperty method returns a MetaProperty instance that you can use to retrieve the value by passing the original instance:
def a = myobj.hasProperty('mypropname')?.getProperty(myobj) ?:
'defaultvalueifpropertymissing'
And then use the Safe navigation operator(?.) and Elvis operator (?:) to avoid the if/else.
The shortest version I could think of is
def a = if (a.hasProperty("mypropname")) a.getProperty("mypropname") else "defaultvalueifmissing"
which obviously repeats the property name twice. Creating your own method is possible but it's limited to your current class.
class MyClass {
String name = "the name"
}
def a = new MyClass()
def getProperty(Object theInstance, String propName, Object defaultValue) {
if (theInstance.hasProperty(propName)) theInstance.getProperty(propName) else defaultValue
}
assert "the name" == getProperty(a, "name", "")
assert "default value" == getProperty(a, "method", "default value")
One can use getProperties() of MetaClass or getProperty() of GroovyObject:
class Test {
String field1
String field2
}
def test = new Test(field1: "value1", field2: null)
// using MetaClass.getProperties()
println test.properties["field1"] // value1
println test.properties["field2"] // null
println "field2" in test.properties.keySet() // true
println test.properties["field3"] // null
println "field3" in test.properties.keySet() // false
// using GroovyObject.getProperty()
println test.getProperty("field1") // value1
println test.getProperty("field2") // null
println test.getProperty("field3") // groovy.lang.MissingPropertyException

Save test case properties if any of the assertions fail

How to save the test case properties if any of the assertions fail within this groovy script step?
Below is example code:
// define properties required for the script to run.
def groovyUtils = new com.eviware.soapui.support.GroovyUtils(context)
def dataFolder = groovyUtils.projectPath
def vTIDAPI = testRunner.testCase.getPropertyValue("vTIDAPI")
def vTIDDB = testRunner.testCase.getPropertyValue("vTIDDB")
def RefAPI = testRunner.testCase.getPropertyValue("RefAPI")
def RefDB = testRunner.testCase.getPropertyValue("RefDB")
def AmountAPI = testRunner.testCase.getPropertyValue("AmountAPI")
def AmountDB = testRunner.testCase.getPropertyValue("AmountDB")
def CurrencyAPI = testRunner.testCase.getPropertyValue("CurrencyAPI")
def CurrencyDB = testRunner.testCase.getPropertyValue("CurrencyDB")
assert vTIDAPI == vTIDDB
assert RefAPI == RefDB
assert AmountAPI == AmountDB
assert CurrencyAPI == CurrencyDB
Here is the Groovy Script which does compare the given set of properties and on any of the assertion failure, writes the properties to a given file.
You need to change the value of property file name to be stored for variable propFileName variable.
Add more properties to be asserted in the form of key:value pairs format if needed
//Provide / edit the file name to store properties
def propFileName = '/tmp/testCase.properties'
//Define the properties to be matched or asserted ; add more properties if needed
def props = [ 'vTIDAPI':'vTIDDB', 'RefAPI':'RefDB', 'AmountAPI': 'AmountDB', 'CurrencyAPI': 'CurrencyDB']
/**
* Do not edit beyond this point
*/
def writeTestCasePropertiesToFile = {
//Get the test case properties as Properties object
def properties = context.testCase.properties.keySet().inject([:]){map, key -> map[key] = context.testCase.getPropertyValue(key); map as Properties}
log.info properties
assert properties instanceof Properties
properties?.store(new File(propFileName).newWriter(), null)
}
def myAssert = { arg1, arg2 ->
context.testCase.getPropertyValue(arg1) == context.testCase.getPropertyValue(arg2) ? null : "${arg1} value does not match with ${arg2}"
}
def failureMessage = new StringBuffer()
props.collect{ null == myAssert(it.key, it.value) ?: failureMessage.append(myAssert(it.key, it.value)).append('\n')}
if(failureMessage.toString()) {
log.error "Assertion failures:\n ${failureMessage.toString()}"
writeTestCasePropertiesToFile()
throw new Error(failureMessage.toString())
} else {
log.info 'Assertions passed'
}
EDIT: Based on the OP comments
Replace def myAssert = ... with below code fragment.
def myAssert = { arg1, arg2 ->
def actual = context.testCase.getPropertyValue(arg1)
def expected = context.testCase.getPropertyValue(arg2)
actual == expected ? null : "${arg1} value does not match with ${arg2} - api ${actual} vs db ${expected}"
}

Node misbehaving with interpolated string

When I create a child node using an interpolated string I am unable to access that node again using dot notation. When I try to access the node in question I just get null. I can get the node if I loop through children() and hunt for it, but I shouldn't have to do that. The following code duplicates the problem:
// All works as expected when an interpolated string isn't used to create the child node
def rootNode = new Node(null, "root")
def childNode = new Node(rootNode, "child", [attr: "test"])
def childNodeCopy = rootNode.child[0]
println childNode.toString() // child[attributes={attr=test}; value=[]]
println childNodeCopy.toString() // child[attributes={attr=test}; value=[]]
println childNode.toString() == childNodeCopy.toString() // true
// But when an interpolated string is used the child node cannot be accessed from the root
rootNode = new Node(null, "root")
def childName = "child"
childNode = new Node(rootNode, "$childName", [attr: "test"])
childNodeCopy = rootNode.child[0]
println childNode.toString() // child[attributes={attr=test}; value=[]]
println childNodeCopy.toString() // null
println childNode.toString() == childNodeCopy.toString() // false
Ahhhh, it's because internally, Node must be storing the node names as keys in a map actually, it just iterates through the names of the nodes, but as it's in Java, it won't find the children as string.equals( groovyString ) will never be true
And as Groovy Strings are not Strings, rootNode.child is returning null
As a workaround, you can do:
childNode = new Node(rootNode, "$childName".toString(), [attr: "test"])
childNodeCopy = rootNode.child[0]

Resources