Insert child node in XML - groovy

need help with simple inserting node after specific one in XML using Groovy. Searching through the existing posts came to that, closer but not enough
import groovy.xml.*
def x='''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns7:setPlayerInfoRequest xmlns:ns7="http://www.playtech.com/services/player-management">
<ns7:behaviourType>Create</ns7:behaviourType>
<ns7:playerDataMap>
<ns7:currency>${p_currency}</ns7:currency>
</ns7:playerDataMap>
</ns7:setPlayerInfoRequest>'''
def n = '''<ns7:custom01>custom01</ns7:custom01>'''
def xml=new XmlParser().parseText(x)
def node = new XmlSlurper(false,false).parseText(n)
def nodes = xml.'**'.findAll{ it.name().localPart == 'currency' }
nodes.each{it.parent().appendNode(node)}
XmlUtil.serialize(xml).toString()
Result
<?xml version="1.0" encoding="UTF-8"?><ns7:setPlayerInfoRequest xmlns:ns7="http://www.playtech.com/services/player-management">
<ns7:behaviourType>Create</ns7:behaviourType>
<ns7:playerDataMap>
<ns7:currency>${p_currency}</ns7:currency>
<custom01/>
</ns7:playerDataMap>
</ns7:setPlayerInfoRequest>
Expected result is to have <ns7:custom01>custom01</ns7:custom01> inserted under parent playerDataMap

You use XmlSlurper to create node from n. But you should use XmlParser as you already do at the line above
You should also use it.parent().append(node) at the line with nodes.each { it.parent().appendNode(node) }
After applying these two changes, it will work as you expect

Related

A better XML Serializer for Python 3

I tried xml_marshaller as follows:
from xml_marshaller import xml_marshaller
class Person:
firstName = "John"
lastName = "Doe"
person1 = Person()
strXmlPerson = xml_marshaller.dumps(person1);
print(strXmlPerson)
The output from above is:
b'<marshal><object id="i2" module="__main__" class="Person"><tuple/><dictionary id="i3"><string>firstName</string><string>John</string><string>lastName</string><string>Doe</string></dictionary></object></marshal>'
which when formatted looks like this, which in my opinion is the ugliest XML possible:
b'<marshal>
<object id="i2" module="__main__" class="Person">
<tuple/>
<dictionary id="i3">
<string>firstName</string>
<string>John</string>
<string>lastName</string>
<string>Doe</string>
</dictionary>
</object>
</marshal>'
What is the b and quotes doing there? Means "binary" maybe? Is that really part of the data, or just a side effect of printing it to the console?
Is there any Python 3 library that will create something more closer to "human" like this:
<Person>
<firstname>John</firstname>
<lastname>Doe<lastname>
</Person>
I'm looking for something close to what .NET creates (see http://mylifeismymessage.net/xml-serializerdeserializer/.
Please don't tell me try JSON or YAML, that's not the question. I might want to run the file through XSLT for example.
Update 2 days later:
I like the Peter Hoffman answer here:
How can I convert XML into a Python object?
person1 = Person("John", "Doe")
#strXmlPerson = xml_marshaller.dumps(person1);
person = objectify.Element("Person")
strXmlPerson = lxml.etree.tostring(person1, pretty_print=True)
print(strXmlPerson)
gives error:
TypeError: Type 'Person' cannot be serialized.
In my scenario I might already have a class structure, and don't want to switch to they way they are doing it You can I serialize my "Person" class?
The reason the output is showing xml as a dictionary is most likely because the properties don't have a reference to the class. You should consider using self. and assigning values within a __init__ function.
class Person:
def __init__(self):
self.firstName = "John"
self.lastName = "Doe"
There are many ways to convert an object to XML. However try using the package dicttoxml. As the name suggest you'll need to convert object to a dictionary, which can be done using vars().
The full solution:
from dicttoxml import dicttoxml
class Person:
def __init__(self):
self.firstName = "John"
self.lastName = "Doe"
person = vars(Person()) # vars is pythonic way of converting to dictionary
xml = dicttoxml(person, attr_type=False, custom_root='Person') # set root node to Person
print(xml)
Output:
b'<?xml version="1.0" encoding="UTF-8" ?><Person><firstName>John</firstName><lastName>Doe</lastName></Person>'
If you want to format the XML nicely, then you can use the built in xml.dom.minidom.parseString library.
from dicttoxml import dicttoxml
from xml.dom.minidom import parseString
class Person:
def __init__(self):
self.firstName = "John"
self.lastName = "Doe"
person = vars(Person()) # vars is pythonic way of converting to dictionary
xml = dicttoxml(person, attr_type=False, custom_root='Person') # set root node to Person
print(xml)
dom = parseString(xml)
print(dom.toprettyxml())
Output:
<?xml version="1.0" ?>
<Person>
<firstName>John</firstName>
<lastName>Doe</lastName>
</Person>
Do check out the documentation https://pypi.org/project/dicttoxml/ as you can pass additional arguments to alter the output.

not able to extract attribute from xml in groovy script

I am trying to automate using groovy script. Here is my script. I am not able to mention namspace.
<ns2:contactPref xmlns="namespace 1" xmlns:ns2="name space 2">
<ns2:Information>
<value>Pass</value>
</ns2:Information>
<ns2:contactPreference>
<ns2:contactPointRel>
<contactPoint xs:type="Tele" xmlns:xs="namespace 3">
<cat>mob</cat>
<med>Int</med>
</contactPoint>
</ns2:contactPointRel>
</ns2:contactPreference>
</ns2:contactPref>
Now I want to fetch attribute at contactPoint tag
for this I have tried like this
groovyUtils=new com.eviware.soapui.support.Groovyutills(context)
def xPath=XPathFactory.newInstance().newXPath()
def type=context.expand('${GetResponse#Response#//*:contactPoint/#xs:type}')
log.info type
tried this
def type=context.expand('${GetResponse#Response#//*:contactPoint/#type}')
and I also tried this
def resp=groovyUtils.getXmlHolder("GetResponse#Response")
def type1=(String)xPath.evaluate('//:contactpoint/#xs:type',resp,xPathConstants.STRING)
but no help. please some one tell me how to do this??
try using this it should work
#{namespace 3}type
if you see xs:type="Tele" xmlns:xs="namespace 3" in contactPoint then xs which has value namespace 3 and {type} is Tele
Edited - You could use something like this - with xmlString as XML
def xml = new XmlSlurper().parseText(xmlString)
println
xml.contactPreference.contactPointRel.contactPoint."#{namespace
3}type"

XMLHolder in groovy unable to Retrieve value

I have the following xml:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SOAP-ENV:Body>
<Login_v1Response>
<result xsi:nil="true"/>
<opSessionID>FjqkjEjipbhkdiin</opSessionID>
</Login_v1Response>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I have the following code in groovy which returns me null:
def groovyUtils=new com.eviware.soapui.support.GroovyUtils(context)
def holder = groovyUtils.getXmlHolder("Step1-Login#response")
log.info holder.getNodeValue("//SOAP-ENV:Envelope/SOAP-ENV:Body/Login_v1Response/opSessionID")
Please help out.
Thanks.
You are using namespaces in your script, without defining what those namespaces are. For just reading values it is generally easier to use wildcards and not worry about them.
def groovyUtils=new com.eviware.soapui.support.GroovyUtils(context)
def holder = groovyUtils.getXmlHolder("Step1-Login#Response")
log.info holder.getNodeValue("//*:opSessionID")
Or even something simpler like:
log.info context.expand('${Step1-Login#Response#//*:opSessionID}')

Using a variable to extract value of property of an element in groovy using xmlSlurper

I am using SoapUI to test webservices. The following string (in xml format) is my request:
<Request>
<AC>
<AssetID>1</AssetID>
<Asset_Name>ABC</Asset_Name>
<Asset_Number>1</Asset_Number>
</AC>
<AC>
<AssetID>2</AssetID>
<Asset_Name>XYZ</Asset_Name>
<Asset_Number>2</Asset_Number>
</Ac>
</Request>
I am using the following code in a groovy script to extract value of Asset_Number for each AC (The above xml string is stored in variable strRequest):
def x = new XmlSlurper().parseText("$strRequest")
x.AC.each { AC ->
assetNum = AC."Asset_Number"
<<do something with the assetNum>>
}
However, I wish to parameterize the above code to pick up Asset_Number for various types of assets (e.g. AC, Peripheral etc). The request xml for each asset is in the same format as above. If I replace 'AC' with variable name 'requestName' in above code:
//strRequest = xml request
def requestName //I will pick up value for this from a test case property
def x = new XmlSlurper().parseText("$strRequest")
x.(requestName.toString()).each { requestName ->
assetNum = requestName."Asset_Number"
<<do something with the assetNum>>
}
it shows the following error:
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed: Script166.groovy: 35: The current scope already contains a variable of the name requestName # line 35, column 2. { ^ org.codehaus.groovy.syntax.SyntaxException: The current scope already contains a variable of the name requestName
I have tried a solution mentioned in another post Using a String as code with Groovy XML Parser, but it doesn't serve my purpose.
Any other ideas?
You can use
x."$requestName".each
Why XmlSlurper? In SoapUI you can use XmlHolder
import com.eviware.soapui.support.XmlHolder
def responseHolder = new XmlHolder(messageExchange.getResponseContentAsXml())
def resultFromResponse = responseHolder["here_is_your_xpath"]
assert someExpectedResult == resultFromResponse
And if you need to iterate via multiple xml nodes
resultFromResponse.each
{
assert result == resultFromResponse
}

Unexpected results with groovy.util.slurpersupport.NodeChild.appendNode() (Groovy 2.2.1)

I think what I am trying to do is simple: Add child nodes dynamically to a node (without even knowing the name of the node to be added - developing some framework) using XmlSlurper.
For ease of explaining, something like this:
def colorsNode = new XmlSlurper().parseText("""
<colors>
<color>red</color>
<color>green</color>
</colors>""")
NodeChild blueNode = new XmlSlurper().parseText("<color>blue</color>") // just for illustration. The actual contents are all dynamic
colorsNode.appendNode(blueNode) // In real life, I need to be be able to take in any node and append to a given node as child.
I was expecting the resulting node to be the same as slurping the following:
“””
<colors>
<color>red</color>
<color>green</color>
<color>blue</color>
</colors>"""
However the result of appending is:
colorsNode
.node
.children => LinkedList[Node('red') -> Node('green') -> <b>NodeChild</b>(.node='blue')]
In other words, what gets appended to the LinkedList is the NodeChild that wraps the new node, not the node itself.
Not surprising, looking at the source code for NodeChild.java:
protected void appendNode(final Object newValue) {
this.node.appendNode(newValue, this);
}
Well, I would gladly modify my code into:
colorsNode.appendNode(blueNode<b>.node</b>)
Unfortunately NodeChild.node is private :(, don't know why! What would be a decent way of achieving what I am trying? I couldn’t see any solutions online.
I was able to complete my prototyping work by tweaking Groovy source and exposing NodeChild.node, but now need to find a proper solution.
Any help would be appreciated.
Thanks,
Aby Mathew
It would be easier if you use XmlParser:
#Grab('xmlunit:xmlunit:1.1')
import org.custommonkey.xmlunit.Diff
import org.custommonkey.xmlunit.XMLUnit
def xml = """
<colors>
<color>red</color>
<color>green</color>
</colors>
"""
def expectedResult = """
<colors>
<color>red</color>
<color>green</color>
<color>blue</color>
</colors>
"""
def root = new XmlParser().parseText(xml)
root.appendNode("color", "blue")
def writer = new StringWriter()
new XmlNodePrinter(new PrintWriter(writer)).print(root)
def result = writer.toString()
XMLUnit.setIgnoreWhitespace(true)
def xmlDiff = new Diff(result, expectedResult)
assert xmlDiff.identical()

Resources