SOAPUI Property Transfer xpath - groovy

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

Related

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

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.

Simple XSLT transformation into ABAP Object

I'm again stuck with a transformation from XML into ABAP. This time, I want to put the XML data directly into an ABAP Object.
My XML looks like this:
<qualityStatus>
<address>0</address>
<bounceRisk>0</bounceRisk>
<checked>1</checked>
<domain>1</domain>
<domainScores>
<domainScore>
<domain>gmx.de</domain>
<score>0.8333333134651184</score>
</domainScore>
<domainScore>
<domain>ggs.de</domain>
<score>0.6666666269302368</score>
</domainScore>
<domainScore>
<domain>xyz.de</domain>
<score>0.6666666269302368</score>
</domainScore>
</domainScores>
<extSyntax>1</extSyntax>
<mailserver>1</mailserver>
<mailserverDiagnosis>1</mailserverDiagnosis>
<probability>1</probability>
<syntax>1</syntax>
</qualityStatus>
Edit: I changed back to a XSLT transformation, shortened to one attribute it looks like this:
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:sap="http://www.sap.com/sapxsl" version="1.0">
<xsl:output encoding="iso-8859-1" indent="yes" method="xml" version="1.0"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/qualityStatus">
<asx:abap version="1.0" xmlns:asx="http://www.sap.com/abapxml">
<asx:values>
<ROOT href="#o26"/>
</asx:values>
<asx:heap xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:abap="http://www.sap.com/abapxml/types/built-in"
xmlns:cls="http://www.sap.com/abapxml/classes/global"
xmlns:dic="http://www.sap.com/abapxml/types/dictionary">
<cls:ZCL_ADDRESS_QUALITY id="o26" >
<local.ZCL_ADDRESS_QUALITY>
<W_ADDRESS>
<xsl:value-of select="address"/>
</W_ADDRESS>
<!--More attributes here-->
</local.ZCL_ADDRESS_QUALITY>
</cls:ZCL_ADDRESS_QUALITY>
</asx:heap>
</asx:abap>
</xsl:template>
My object attributes are all public right now, because I thought this could be the problem. However, setter and getter do exist. Yes, my class does implement the interface if_serializable_object.
DATA:
w_address TYPE char1,
w_bouncerisk TYPE char1,
w_checked TYPE char1,
w_decoded TYPE stringval,
w_domain TYPE char1,
w_domainscores TYPE z_domainscore_t, "Table type for name + score
w_extsyntax TYPE char1,
w_mailserver TYPE char1,
w_mailserverdiagnosis TYPE char1,
w_probability TYPE char1,
w_syntax TYPE char1,
w_syntaxwarnings TYPE z_syntaxwarnings_t. "Table of syntaxwarnings
Finally, I call my transformation with an instance of my class:
CALL TRANSFORMATION zst_addressquality
SOURCE XML lw_xml
RESULT result = lo_addressquality.
Now, when debugging through the transformation code, it successfully notices all fields of the given lw_xml and appears to write them into the object lo_addressquality. But the object attributes stay empty afterwards.
When testing the serialization, I can access result which contains my object, but result-w_address (and all others) are empty.
While testing, I created a structure with completely identical names and types. With it, it worked as intended.
What am I missing? Is there anything else I have to watch out for when working with transformation into ABAP Objects?
_Edit: After changing to the XSLT, I can get until W_ADDRESS before my code throws an CX_XSLT_ABAP_CALL_ERROR. So, I'm still not able to access the object'S attributes properly. :|_
Objects can be serialized/deserialized only with an XSL transformation. It's not possible to do it with a simple transformation, dixit ABAP documentation:
ST programs are restricted to the transformation of elementary and structured ABAP data, along with internal tables. The transformation of reference variables and referenced objects is not currently supported.
The XSL transformation must convert the XML into ASXML, which in short corresponds to a structure like this:
<?xml ...?>
<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
<asx:values>
...
</asx:values>
<asx:heap>
...
</asx:heap>
</asx:abap>
The easiest way to understand what the ASXML should look like is to serialize your object reference using the identity transformation (it's an XSL transformation), and then adapt your transformation to produce the same kind of asXML:
CALL TRANSFORMATION id SOURCE anyRootName = yourObjectReference RESULT XML asXMLutf8xstring.
Example:
REPORT.
CLASS serialization_demo DEFINITION.
PUBLIC SECTION.
INTERFACES if_serializable_object.
DATA attribute TYPE i.
ENDCLASS.
START-OF-SELECTION.
DATA obj_ref TYPE REF TO serialization_demo.
DATA xstring TYPE xstring.
CREATE OBJECT obj_ref.
obj_ref->attribute = 5.
CALL TRANSFORMATION id " serialize
SOURCE root = obj_ref
RESULT XML xstring.
CLEAR obj_ref.
CALL TRANSFORMATION id " deserialize
SOURCE XML xstring
RESULT root = obj_ref.
ASXML (in the xstring variable):
<?xml version="1.0" encoding="utf-8"?>
<asx:abap version="1.0" xmlns:asx="http://www.sap.com/abapxml">
<asx:values>
<ROOT href="#o3"/>
</asx:values>
<asx:heap xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:abap="http://www.sap.com/abapxml/types/built-in" xmlns:cls="http://www.sap.com/abapxml/classes/global" xmlns:dic="http://www.sap.com/abapxml/types/dictionary">
<prg:SERIALIZATION_DEMO id="o3" xmlns:prg="http://www.sap.com/abapxml/classes/program/ZZSRO_TEST16I">
<local.SERIALIZATION_DEMO>
<ATTRIBUTE>5</ATTRIBUTE>
</local.SERIALIZATION_DEMO>
</prg:SERIALIZATION_DEMO>
</asx:heap>
</asx:abap>

How to get the Structure/Template id by Structure/Template name

I have a requirement that, Need to create JournalArticle with Structure and Template.While creating JournalArticle the method expecting the StructureId and TemplateId but these are generated by Liferay.So by name how can i get Id's of both.
Create and execute a DynamicQuery, like so (just replace Template with Structure to get structures):
DynamicQuery q = DynamicQueryFactoryUtil.forClass(DDMTemplate.class)
.add(PropertyFactoryUtil.forName("name").like("%YOUR NAME%"));
List<DDMTemplate> templates = DDMTemplateLocalServiceUtil.dynamicQuery(q);
You have to use like since the names of the structures/templates are saved like so:
<?xml version='1.0' encoding='UTF-8'?>
<root available-locales="de_DE" default-locale="de_DE">
<Name language-id="de_DE">YOUR NAME</Name>
</root>
There can be different names for different locales.
You can get StructureId (called DDMStructure) with this code
long classNameIdJournalArticle = ClassNameLocalServiceUtil.getClassNameId(JournalArticle.class);
DDMStructure ddmStructure = DDMStructureLocalServiceUtil.getStructure(groupId, classNameIdJournalArticle, "myDDMStructureName");
And TemplateId (called DDMTemplate) with this code
DDMTemplate ddmTemplate = DDMTemplateLocalServiceUtil.getTemplate(groupId, classNameIdDDMStructure, "ddmTemplateName");

access response in SOAP UI in Groovy Script

I am new to Groovy Scripting. I am trying to access the value of a Response node
below is the script
def groovyUtils = new com.eviware.soapui.support.GroovyUtils(context);
def responseHolder = groovyUtils.getXmlHolder( testRunner.testCase.testSteps["request"].testRequest.response.responseContent );
responseHolder.namespaces["ns0"]="http://xmlns.int.com/orders/xsd/v1"
String mySection = responseHolder.getNodeValue["//ns0:MT_OrderCreateDTCFulfillmentResponse/ns0:StatusCode"] ;
log.info mySection
mySection is printed as []
Response XML:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header xmlns:v1="http://xmlns.int.com/orders/xsd/v1"/>
<soapenv:Body xmlns:v1="http://xmlns.int.com/orders/xsd/v1">
<ns0:MT_OrderCreateDTCFulfillmentResponse xmlns:ns0="http://xmlns.int.com/orders/xsd/v1">
<StatusCode>000</StatusCode>
<ReferenceDocNbr>NA</ReferenceDocNbr>
<SchemaValidationStatus>Validated</SchemaValidationStatus>
<StatusTimestamp>2015-08-03T18:58:01.602</StatusTimestamp>
<FaultDetails>Request for customer order number NA received successfully and format validated.</FaultDetails>
</ns0:MT_OrderCreateDTCFulfillmentResponse>
</soapenv:Body>
</soapenv:Envelope>
SOAP UI Project Structure - I am running Test_Script. Suggest me what i am missing
You've to use:
responseHolder.getNodeValue("//ns0:MT_OrderCreateDTCFulfillmentResponse/StatusCode");
instead of responseHolder.getNodeValue["//ns0:MT_OrderCreateDTCFulfillmentResponse/ns0:StatusCode"];
Note that I change responseHolder.getNodeValue invocation to use () instead of [], and also change your xpath since in your response <StatusCode> it's not defined in xmlns:ns0="http://xmlns.int.com/orders/xsd/v1".
Another option is to use the * wildcard as a namespace to map anyone. So in this case you can use:
responseHolder.getNodeValue("//*:MT_OrderCreateDTCFulfillmentResponse/*:StatusCode");
Additionally, note that probably you XML is wrong, since I suppose that all sub elements of <MT_OrderCreateDTCFulfillmentResponse> must belongs to "http://xmlns.int.com/orders/xsd/v1" namespace... so you've to declare it as:
<ns0:MT_OrderCreateDTCFulfillmentResponse xmlns:ns0="http://xmlns.int.com/orders/xsd/v1">
<ns0:StatusCode>000</ns0:StatusCode>
<ns0:ReferenceDocNbr>NA</ns0:ReferenceDocNbr>
<ns0:SchemaValidationStatus>Validated</ns0:SchemaValidationStatus>
<ns0:StatusTimestamp>2015-08-03T18:58:01.602</ns0:StatusTimestamp>
<ns0:FaultDetails>Request for customer order number NA received successfully and format validated.</ns0:FaultDetails>
</ns0:MT_OrderCreateDTCFulfillmentResponse>
Or using as default for this tag:
<MT_OrderCreateDTCFulfillmentResponse xmlns="http://xmlns.int.com/orders/xsd/v1">
<StatusCode>000</StatusCode>
<ReferenceDocNbr>NA</ReferenceDocNbr>
<SchemaValidationStatus>Validated</SchemaValidationStatus>
<StatusTimestamp>2015-08-03T18:58:01.602</StatusTimestamp>
<FaultDetails>Request for customer order number NA received successfully and format validated.</FaultDetails>
</MT_OrderCreateDTCFulfillmentResponse>
Note that if you change you XML with my indication your first XPath it's correct since now StatusCode belongs to your namespace.
Hope it helps,

Resources