Parsing an XML in Groovy through XML Slurper - groovy

I am having this strange problem of parsing an xml through XMLSlurper in groovy and it shows the size as 0. Cannot figure out why.
My xml file looks like:
<?xml version="1.0" encoding="iso-8859-1"?>
<sites>
<site name="OctUK">
<property name="warName">OctUKbuild-Deployable</property>
</site>
<site name="GbsJP">
<property name="warName">GbsJPbuild-Deployable</property>
</site>
</sites>
Code:
findSite("${project.GTA_BUILD_HOME}/platforms/pos/config/pos-sites.xml")
//Passed the path of the xml file to the method below:
GPathResult findSite(String sitesXml) {
xmlConfig = new XmlSlurper().parse(new File(sitesXml))
def siteGPath = xmlConfig.sites.site.findAll
// Check that a POS-sites.xml is valid
assert siteGPath.size() != 0, 'Error: no site found'
return(siteGPath)
}
The method fails with the error saying Error: no site found, because it is giving the result of siteGPath as 0. Not sure why it is giving the result as 0. It should have the size as 2.
Is there anything wrong I am doing. Any help is much appreciated. I am stuck at this point.

You don't need sites when looking at the xmlConfig object.
sites is the root node, so is implied, try:
assert xmlConfig.site.size() == 2
Also, xmlConfig.site is a instance of NodeChildren, but you seem to be declaring a return type of GPathResult
And I'm not sure what's missing from the end of your findAll call, as that should take a Closure, or empty parentheses

Related

In groovy Xpath function not working for a xml

I am having the below XML:
<orders>
<order xmlns:amz="http://www.amazon.co">
<amz:comp>amz</amz:comp>
</order>
<order xmlns:ebay="http://www.ebay.co">
<ebay:comp>ebay</ebay:comp>
</order>
</orders>
I checked the xpath expressions like //orders/order it is working but in groovy it is not working I'm not getting what is wrong.
Below is the code that I am using:
import groovy.xml.*;
def source = '''<orders>
<order xmlns:amz="http://www.amazon.co">
<amz:comp>amz</amz:comp>
</order>
<order xmlns:ebay="http://www.ebay.co">
<ebay:comp>ebay</ebay:comp>
</order>
</orders>'''
def root = new XmlSlurper().parseText(source).declareNamespace([
amz: "http://www.amazon.co",
ebay: "http://www.ebay.co"
])
println root.orders
println root.orders.order."amz:comp"
I am not getting any output for the above code.
In your example, root variable refers to the <orders> element, so when you call root.orders it is like you were looking for
<orders>
<orders>...</orders>
</orders>
Rename root variable to orders and do
println orders
println orders.order."amz:comp"
to get the following output:
amzebay
amz

How to access XML node containing a period using groovy.util.XmlParser

I am needing to read a XML file where certain node names contain periods here is an example:
<build>
<actions>
<hudson.model.ParametersAction>
<safeParameters class="sorted-set"/>
<parameters>
<hudson.model.StringParameterValue>
...
When I try to use the standard build.actions.hudson.model.ParametersAction.parameters I get
groovy.lang.MissingPropertyException: Exception evaluating property 'hudson' for java.util.concurrent.CopyOnWriteArrayList, Reason: groovy.lang.MissingPropertyException: No such property: hudson for class: hudson.model.ParametersAction
I also tried build.actions.'hudson.model.ParametersAction'.parameters with this error:
groovy.lang.MissingPropertyException: Exception evaluating property 'hudson.model.ParametersAction' for java.util.concurrent.CopyOnWriteArrayList, Reason: groovy.lang.MissingPropertyException: No such property: hudson.model.ParametersAction for class: hudson.model.ParametersAction
How can I access this property?
You are right that it's necessary to surround a "periodish" element with quotes. The below code works for me. If it still doesn't work for you, it will be better if you share the code snippet which instantiates XmlParser and parses the xml.
def xml = """
<build>
<actions>
<hudson.model.ParametersAction>
<safeParameters class="sorted-set"/>
<parameters>
<hudson.model.StringParameterValue>foo</hudson.model.StringParameterValue>
</parameters>
</hudson.model.ParametersAction>
</actions>
</build>
"""
def parser = new XmlParser()
def build = parser.parseText(xml)
println(build.actions.'hudson.model.ParametersAction'.parameters.'hudson.model.StringParameterValue'.text())
//prints foo as a result

How to access xml field with lxml?

Python 3.6, Lxml, Windows 10
I am getting crazy. I want to access the item field. But I always get the error:
AttributeError: 'cython_function_or_method' object has no attribute'item'
Everything else (address fields etc...) I can access without problems. How can I access the item fields (sku, amount etc...)?
I've used this code:
import requests
from lxml import objectify
url = "URL_TO_XML_FILE"
xml_content = requests.get(url).text.encode('utf-8')
xml = objectify.fromstring(xml_content)
for sale in xml.response.sales.sale:
for item in sale.items.item:
print(item.sku)
Here is the beginning of the xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<getnewsalesresult xmlns="https://pmcdn.priceminister.com/res/schema/getnewsales">
<request>
<version>2017-08-07</version>
<user>SELLER</user>
</request>
<response>
<lastversion>2017-08-07</lastversion>
<sellerid>95029358</sellerid>
<sales>
<sale>
<purchaseid>297453287592813953</purchaseid>
<purchasedate>15/12/2018-19:10</purchasedate>
<deliveryinformation>
<shippingtype>Normal</shippingtype>
<isfullrsl>N</isfullrsl>
<purchasebuyerlogin><![CDATA[LOGIN]]></purchasebuyerlogin>
<purchasebuyeremail>EMAIL</purchasebuyeremail>
<deliveryaddress>
<civility>Mme</civility>
<lastname><![CDATA[Lastname]]></lastname>
<firstname><![CDATA[Firstname]]></firstname>
<address1><![CDATA[STREET]]></address1>
<address2><![CDATA[]]></address2>
<zipcode>13570</zipcode>
<city><![CDATA[Paris]]></city>
<country><![CDATA[France]]></country>
<countryalpha2>FX</countryalpha2>
<phonenumber1></phonenumber1>
<phonenumber2>PHONENUMBER</phonenumber2>
</deliveryaddress>
</deliveryinformation>
<items>
<item>
<sku><![CDATA[SKU1]]></sku>
<advertid>411812243030</advertid>
<advertpricelisted>
<amount>15.99</amount>
<currency>EUR</currency>
</advertpricelisted>
<itemid>551131040</itemid>
<headline><![CDATA[HEADLINE]]></headline>
<itemstatus><![CDATA[REQUESTED]]></itemstatus>
<ispreorder>N</ispreorder>
<isnego>N</isnego>
<negotiationcomment></negotiationcomment>
<price>
<amount>15.99</amount>
<currency>EUR</currency>
</price>
<isrsl>N</isrsl>
<isbn></isbn>
<ean>4363745894373857474; </ean>
<paymentstatus><![CDATA[INCOMING]]></paymentstatus>
<sellerscore></sellerscore>
</item>
</items>
</sale>
<sale>
The problem is that items is actually a method of ObjectifiedElement, so the expression sale.items actually returns the method, because it has precedence.
To get the 'items' object you want, you have to be more explicit about getting the attribute of sale and not looking for methods of the class first, which is the usual python order. This is what python does behind the scene when you access an attribute, and you can do it too:
sale.__getattr__('items')
This will also work (it's a dictionary-like interface to the attributes of an object):
sale.__dict__['items']
The revised code:
import requests
from lxml import objectify
url = "URL_TO_XML_FILE"
xml_content = requests.get(url).text.encode('utf-8')
xml = objectify.fromstring(xml_content)
for sale in xml.response.sales.sale:
for item in sale.__dict__['items'].item:
print(item.sku)
Another way to deal with this is to avoid using the flaky attribute interface:
for sale in xml['response']['sales']['sale']:
for item in sale['items']['item']:
print(item['sku'])
Using the dict-like indexing interface, you never have to worry about certain attributes names (which includes such common words as items, index, keys, remove, replace, tag, set, text, and values) returning surprising results.

SOAPUI Property Transfer xpath

I am trying to transfer the response from one test case to another in SOAPUI. So that i can search by country and retrieve the currency code to use then search by currency code. However i am not sure i am getting my xpath correct.
SOAP WSDL used: http://www.webservicex.net/CurrencyConvertor.asmx?WSDL
This is a practice for a service which is shortly due to be delivered.
Initial test case post
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:web="http://www.webserviceX.NET">
<soap:Header/>
<soap:Body>
<web:GetCurrencyByCountry>
<!--Optional:-->
<web:CountryName>Belgium</web:CountryName>
</web:GetCurrencyByCountry>
</soap:Body>
</soap:Envelope>
Response
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<GetCurrencyByCountryResponse xmlns="http://www.webserviceX.NET">
<GetCurrencyByCountryResult><![CDATA[<NewDataSet>
<Table>
<Name>Belgium</Name>
<CountryCode>be</CountryCode>
<Currency>Franc</Currency>
<CurrencyCode>BEF</CurrencyCode>
</Table>
<Table>
<Name>Belgium</Name>
<CountryCode>be</CountryCode>
<Currency>Franc</Currency>
<CurrencyCode>BEF</CurrencyCode>
</Table>
</NewDataSet>]]></GetCurrencyByCountryResult>
</GetCurrencyByCountryResponse>
</soap:Body>
</soap:Envelope>
I am then using the property transfer test step, Firstly setting the drop downs as follows:
Source: Get Currency by Country; Property: Response Path Language: xpath
Then declared the xpath as follows:
declare namespace sam= 'http://www.webserviceX.NET';//GetCurrencyByCountryResult/table[2]/CurrencyCode
Each time i run the test i get a Null response. I have attempted using a wild card however i still cannot pick up the value.
Here is the groovy script which does exactly what you are looking for:
Added comments appropriately before each statement for better understanding.
Currently using the fixed xml response. But you may replace it to make it dynamic.
Since your xml has cdata and cdata has xml again. So, this needs to be done in two phases, first time cdata and then xpath to get the actual value that is needed.
import com.eviware.soapui.support.XmlHolder
def xml = '''<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Body>
<GetCurrencyByCountryResponse xmlns="http://www.webserviceX.NET">
<GetCurrencyByCountryResult><![CDATA[<NewDataSet>
<Table>
<Name>Belgium</Name>
<CountryCode>be</CountryCode>
<Currency>Franc</Currency>
<CurrencyCode>BEF</CurrencyCode>
</Table>
<Table>
<Name>Belgium</Name>
<CountryCode>be</CountryCode>
<Currency>Franc</Currency>
<CurrencyCode>BEF</CurrencyCode>
</Table>
</NewDataSet>]]></GetCurrencyByCountryResult>
</GetCurrencyByCountryResponse>
</soap:Body>
</soap:Envelope>'''
//If you want this script to handle dynamic response
// remove above xml and uncomment below line and replace your previous step name in
// place of STEP_NAME
//def xml = context.expand( '${STEP_NAME#Response}' )
//Please replace the actual response in place of xml
def holder = new XmlHolder(xml)
//Get the CDATA part
def countryCodeResult = holder.getNodeValue('//*:GetCurrencyByCountryResult')
//log the content of CDATA
log.info countryCodeResult
//Again convert cdata string as xml holder
def cdataHolder = new XmlHolder(countryCodeResult)
//Get the actual xpath
def currencyCode = cdataHolder.getNodeValue("//Table[2]/CurrencyCode")
log.info currencyCode
//now you may store this currency code as suite level propery so that
//you can use it in next test case as ${#TestSuite#CURRENCY_CODE}
context.testCase.testSuite.setPropertyValue('CURRENCY_CODE', currencyCode)
As you see in the above comment, the script is storing currency code into a test suite level property. That allows you to use the currency code in different test case or step using property expansion, say ${#TestSuite#CURRENCY_CODE}
You need to store the CDATA Content in to a property and then use that property value in groovy script and access CDATA internal xml, I am referring in the below example <![CDATA[<isle>test</isle>]]> .
eg:
<test>
<elementa>
<![CDATA[<isle>test</isle>]]>
</elementa>
</test>
In your current scenario Soapui only understand XPATH up to "//GetCurrencyByCountryResult" , after that whatever path have provided goes under CDATA .
SOAPUI working with CDATA is separate mechanism.
Please look into this; it has lot of information on CDATA with SOAPUI and its definitely solve your issue
https://www.soapui.org/functional-testing/working-with-cdata.html

Parse XML using Groovy: Override charset in declaration and add XML processing instruction

My initial question have been answered, but that did just open up for further issues.
Example code
Using Groovy 2.0.5 JVM 1.6.0_31
import groovy.xml.*
import groovy.xml.dom.DOMCategory
def xml = '''<?xml version="1.0" encoding="UTF-16"?>
| <?xml-stylesheet type="text/xsl" href="Bp8DefaultView.xsl"?>
|<root>
| <Settings>
| <Setting name="CASEID_SEQUENCE_SIZE">
| <HandlerURL>
| <![CDATA[ admin/MainWindow.jsp ]]>
| </HandlerURL>
| </Setting>
| <Setting name="SOMETHING_ELSE">
| <HandlerURL>
| <![CDATA[ admin/MainWindow.jsp ]]>
| </HandlerURL>
| </Setting>
| </Settings>
|</root>'''.stripMargin()
def document = DOMBuilder.parse( new StringReader( xml ) )
def root = document.documentElement
// Edit: Added the line below
def pi = document.createProcessingInstruction('xml-stylesheet', 'type="text/xsl" href="Bp8DefaultView.xsl"');
// Edit #2: Added line below
document.insertBefore(pi, root)
use(DOMCategory) {
root.Settings.Setting.each {
if( it.'#name' == 'CASEID_SEQUENCE_SIZE' ) {
it[ '#value' ] = 100
}
}
}
def outputfile = new File( 'c:/temp/output.xml' )
XmlUtil.serialize( root , new PrintWriter(outputfile))
// Edit #2: Changed from root to document.documentElement to see if that
// would make any difference
println XmlUtil.serialize(document.documentElement)
Problem description
I'm trying to parse a XML-file exported from a third party tool, and before promoting it to stage and production I need to replace certain attribute values. That is all ok, but in addition I must keep the encoding and ref. to the stylesheet.
Since this is pretty static it is totally ok to have both the encoding and the stylesheet ref. definition in a property-file, meaning: I do not need first to find the declarations in the original file.
Encoding in declaration issue
As shown in this answer found here on StackOverFlow you can do
new File('c:/data/myutf8.xml').write(f,'utf-8')
I have also tried
XmlUtil.serialize( root , new GroovyPrintStream('c:/temp/output.txt', 'utf-16'))
but it did not solve the problem for me either. So I have not understood how to override the UTF-value.
Processing instruction issue
Simply, how do I add
<?xml-stylesheet type="text/xsl" href="Bp8DefaultView.xsl"?>
to the output?
Update - This can be done like this
def pi = document.createProcessingInstruction('xml-stylesheet', 'type="text/xsl" href="Bp8DefaultView.xsl"');
The processing instruction is being added like this, this guideline showed me, but still I do not get the output.
document.insertBefore(pi, root) // Fails
All issues in this question has been answered in another question I raised, see Groovy and XML: Not able to insert processing instruction
The trick is that I expected
document.documentElement
to contain the processing instruction. But that is wrong, documentElement is:
...This is a convenience attribute that allows direct access to the child node that is the document element of the document...
Where the processing instruction is just another child node. So what I instead had to use was either the LSSerializer or the Transfomer. See Serialize XML processing instruction before root element for details.

Resources