Copy attribute values based on a preceding node attribute with Groovy - groovy

I'm trying to do something in SoapUi with Groovy and it's not working.
I have multiple nodes with many attributes and I need to extract the child node attribute based of a parent's attribute
For example:
<library id="82389389">
<book id="123" bookType="SF">
<price id="325" priceValue="5"/>
</book>
<book id="4741" bookType="History">
<price id="12388" priceValue="15"/>
</book>
<book id="2626" bookType="Drama">
<price id="12145" priceValue="40"/>
</book>
</library>
In this XML I need to extract priceValue based on the bookType and use it somewhere else (the order of the book nodes is changing)
I tried this but it doesn't work:
def response = .../library[1]
def i=0
def records = new XmlSlurper().parseText(response)
def size = records.book.size()
for (i=0,i<size,i++)
{
bookType1 = records.book[i].#bookType.first().value().text();
if (bookType1 == 'History')
{
def priceValueBook = records.book[i].price.#priceValue.first().value().text()
log.info priceValueBook
}
}

It's not clear exactly at what point you are trying to do this, but the following would work in SoapUI script step:
context.expand('${test_step#Response#//*:book[#bookType="History"]/*:price/#priceValue}')

Related

XML Handling using X-path using Java/groovy script

I have a very deep/nested structure xml where I need to filter only those fields which will be declared in some store(parameter let's say). Or the other solution is , I need to make those field values as "X" which are not stored in the parameters. As the structure is very deep, I suppose the field should be declared in the parameter as x-path.
Please suggest some clues.
I am using the below code now but this is removing all the fields under different nodes as well. I want to remove this from a particular node. For example(as shown below) field 'action' is present in many nodes but I want to remove this from some specific nodes.
Or I want to set this field value as "X".
def xml = new XmlParser().parseText(body)
def nodes = xml.'**'.findAll{ node -> node.name() == 'action' }
//nodes.each{it.text()}
def removeNode = { node ->
def parent = node.parent()
parent.remove(node)
}
nodes.each{removeNode(it)}
def final_body = groovy.xml.XmlUtil.serialize(xml);
My input data is as follows:
<?xml version='1.0' encoding='UTF-8'?>
<query>
<Emp>
<id>34032</id>
<ps>
<action>INSERT</action>
<id>34032</id>
<PI>
<action>INSERT</action>
<gen>M</gen>
<ms>S</ms>
<PI_mex>
<action>INSERT</action>
<genericNumber1>0</genericNumber1>
<genericNumber2>0</genericNumber2>
<genericNumber3>0</genericNumber3>
<genericNumber4>0</genericNumber4>
<genericNumber5>0</genericNumber5>
<genericNumber6>0</genericNumber6>
<genericNumber7>0</genericNumber7>
</PI_mex>
</PI>
<PI>
<action>INSERT</action>
<gen>M</gen>
<ms>S</ms>
<PI_mex>
<action>INSERT</action>
<genericNumber1>0</genericNumber1>
<genericNumber2>0</genericNumber2>
<genericNumber3>0</genericNumber3>
<genericNumber4>0</genericNumber4>
<genericNumber5>0</genericNumber5>
<genericNumber6>0</genericNumber6>
<genericNumber7>0</genericNumber7>
</PI_mex>
</PI>
<ai>
<action>INSERT</action>
<city>SAN NICOALAS DE LOS GARZA</city>
<country>MEX</country>
<state>NLE</state>
<zip_code>66420</zip_code>
</ai>
<ai>
<action>INSERT</action>
<city>SAN NICOALAS DE LOS GARZA</city>
<country>MEX</country>
<state>NLE</state>
<zip_code>66420</zip_code>
</ai>
<ei>
<action>INSERT</action>
<id>26658</id>
<user_id>00860884</user_id>
<ji>
<action>INSERT</action>
<c_string10>5</c_string10>
<c_string11>P</c_string11>
<c_string12>MX_F_1E</c_string12>
</ji>
<ji>
<action>INSERT</action>
<c_string10>5</c_string10>
<c_string11>P</c_string11>
<c_string12>MX_F_1E</c_string12>
<c_string13>1310</c_string13>
<c_string16>90000269</c_string16>
<c_string17>Y</c_string17>
<c_string18>BCC</c_string18>
</ji>
<CI>
<action>INSERT</action>
<pg>M9</pg>
<pr>
<action>INSERT</action>
<pc>GL1003</pc>
<pc_type>AMOUNT</pc_type>
</pr>
</CI>
</ps>
</Emp>
</query>
Thanks.

how to get child node value from a paren preferenced by another child value in Groovy readyAPI

I am trying to get values from a web service response in readyAPI, so i can pass it to another web service request, so i can create a automated test flow.
I have tried different code pieces most of them was a single line of code, which i prefer if it possible. I can take value from a node by typing the parent node by its attribute value. I also can get parent node by child nodes attribute value and use it to get another child value.
Here some examples:
First Format that I can use it to get childs value:
<webserviceResponse>
<documentslist>
<document #id="1">
<payment #currency="USD" >
<amount>1250.00</amount>
</payment>
</document>
<document #id="2">
<payment #currency="JPY" >
<amount>150.00</amount>
</payment>
</document>
<document #id="3">
<payment #currency="EUR" >
<amount>1170.00</amount>
</payment>
</document>
<!-- etc. -->
</documentslist>
-----> To get currency for a specific document
def webServiceResponse = "webservice#Response"
int index=2
def currency = context.expand('${'+webServiceResponse+'//*:document[#id="['+index+']"]//*:payment/#currency}')
-----> Result of this is "JPY"
<webserviceResponse>
<documentslist>
<document #id="1">
<payment #currency="USD" >
<amount>1250.00</amount>
</payment>
<refund>true</refund>
</document>
<document #id="2">
<payment #currency="JPY" >
<amount>150.00</amount>
</payment>
</document>
<document #id="3">
<payment #currency="EUR" >
<amount>1170.00</amount>
</payment>
<refund>false</refund>
</document>
<!-- etc. -->
</documentslist>
-------> To get a currency dependent on existence of a specific node
In this example we are looking the file from up to down and we are finding every refund nodes,
and taking currency value that is in the same block with the second time we see a refund node.
def webServiceResponse = "webservice#Response"
int index=2
def currrency= context.expand('${'+webServiceResponse+'(//*:refund)['+index+']//parent::*//*:payment/#currency}')
--------> Result for this is "EUR"
This one is that i cant take child value with the same way.
<webserviceResponse>
<documentslist>
<document>
<key>D_Computer</key>
<currency>USD</currency>
<amount>1250.00</amount>
<refund>true</refund>
</document>
<document>
<key>D_Keyboard</key>
<currency>JPY</currency>
<amount>150.00</amount>
</document>
<document>
<key>D_Monitor</key>
<currency>EUR</currency>
<amount>1170.00</amount>
<refund>false</refund>
</document>
<!-- etc. -->
</documentslist>
My problem with this one it doesn't have any attributes, has only values of the nodes. I know that it doesnt have an integer by the way but maybe i am doing wrong that i dont realize.
I want to get the amount value only dependent to the "key" nodes value which i am going to specify in the script.
result should show :150.00
Thank you for the very detailed and well written question.
You can use the below. Your problem is easy as there are no namespace in it.
Technique is same which you have dispalyed, its just that you need not to use # as its for attributes
def groovyUtils=new com.eviware.soapui.support.GroovyUtils(context)
def xml=groovyUtils.getXmlHolder("NameOfRequest#Response");
def currency=xml.getNodeValue("//*:documentslist/*:document[key='${key}']/*:amount");
log.info "Value of $key is " + currency
key="D_Monitor"
currency=xml.getNodeValue("//*:documentslist/*:document[key='${key}']/*:amount");
log.info "Value of $key is " + currency
Replace NameOfRequest with your Request's name
There is an alternative way too. I will post it as a separate answer so not to cause confusion. This one is still better than other one
There is an alternate way of doing things using Hashmap if the other answer is not working due to namespaces in your XML
Try this method
We are getting all values first by using getNodeValues and then since we have pair we are putting in hashmap.
Now you can retrieve anything.
def groovyUtils=new com.eviware.soapui.support.GroovyUtils(context)
def xml=groovyUtils.getXmlHolder("Request1#Response");
def keys=xml.getNodeValues("//*:documentslist/*:document/*:key")
def amounts=xml.getNodeValues("//*:documentslist/*:document/*:amount")
log.info keys.toString()
log.info amounts.toString()
HashMap h1=[:]
// Add the pair into hashmap and then retrieve
for(int i=0;i<keys.size();i++)
{
h1.put(keys[i],amounts[i])
}
def whichone="D_Computer"
log.info "Value for $whichone is " + h1.get(whichone)
Lets say you want to retrieve more than one value then you can use arrays.
i.e. take arrays as key,currency,amount,refund
so if you want to retrieve the refund for a key='Z' So using a for loop you can know that Z is present at 3 location in the array
then your refund should be refund[3]. Similarly currency[3] and amount[3]
Both the answers have their own relevance

Groovy - XmlSlurper - find innermost element

I have the following xml:
<vehicle>
<car>
<price>100</price>
<price>200</price>
</car>
<car>
<price>300</price>
<price>400</price>
</car>
</vehicle>
Given an xml, how can we get the innermost elements (in this case, all the<price> elements)?
Assuming you have the xml in a String xml, you should be able to do:
List prices = new XmlSlurper().parseText( xml ).car.price*.text()​​
thanks Tim for the answer. I just figured out the following works too. And is more generic:
def document = slurper.parseText(xml)
def prices = document.'**'.findAll { it.children().size() == 0 }
May I suggest you next variant:
def vehicle = new XmlSlurper().parseText(xmlString)
vehicle.car.price.each {println "car's price:"+it}

Groovy MarkupBuilder

I have a list of things, each of which might be a foo or a bar. I want to build some xml that looks like this:
<rdf:RDF>
<foo id="1">
<foo id="2">
<bar id="3">
</rdf:RDF>
So I have gotten this far:
MarkupBuilder xml = new MarkupBuilder(writer)
xml.'rdf:RDF' (nsmap) { }
But now I am stuck. How do i - within that xml.'rdf:RDF' (nsmap) { } closure - iterate over my list of stuff? How do i - within that iterator - spit out a foo or a bar element as applicable?
Its simpler as you may think. Include a loop in the xml closure and in turn include markup in the loop. This script ...
import groovy.xml.MarkupBuilder
things = ['foo','foo','bar']
writer = new StringWriter()
xml = new MarkupBuilder(writer)
xml.'rdf:RDF' {
things.eachWithIndex {thing,index ->
"$thing" id:index+1
}
}
println writer
... will produce following output:
<rdf:RDF>
<foo id='1' />
<foo id='2' />
<bar id='3' />
</rdf:RDF>
Here You go:
import groovy.xml.MarkupBuilder
import org.custommonkey.xmlunit.*
def data = [foo:1,bar:2,baz:3]
def writer = new StringWriter()
def xml = new MarkupBuilder(writer)
xml.'rdf:RDF' {
data.each { e ->
"$e.key"(id:e.value)
}
}
println writer
Ok.
The issue is that when a closure is run by a builder, it operates within the context of the builder. Now, it turns out that the builder context hides methods, but does not hide variables. This means that you can create a closure, assign it to a variable, and access it from within the builder.
NOw, I get the impression that this closure, too, runs in the context of the builder. But I suspect you can assign "this" to a variable outside the closures and then access that variable to get the context back.
It's ridiculous that you have to go to this sort of trouble. Im reminded of coding in Microsoft Access, back in the 90's. It works great, until the moment when you want to do something more than it was strictly designed to do.

How to get a child node value based on another child in the same group

I'm developing in C#, and am processing xml from an external soap call.
I have loaded the xml response into an XElement.
Given the following xml stub
<record>
<node>
<a>My title</a>
<name>title_en</name>
</node>
<node>
<a>...</a>
<name>contact_name</name>
</node>
.....
</record>
Using xpath in C#: I'm trying to do the follow when using the method XPathSelectElement.
where
\record\node\name == 'title_en' select \record\node\a
If there is a better method to use or another suggestion on how to preform the query, I'm open to ideas.
Thanks in advance.
You need a predicate to constrain which node elements you need:
/record/node[name = 'title_en']/a
You read this expression as "find the record element, find all its child elements named node that have a name child with value "title_en", and for each of those find all a children"
Use this:
var title = doc.Descendants("node")
.Where(x => (string)x.Element("name") == "title_en")
.Select(x => (string)x.Element("a"))
.FirstOrDefault();

Resources