Xml goes blank, when updated with Groovy XmlParser - groovy

I am quite new to groovy and seek your help.
I want to read an delta xml file and update master xml file based on a name attribute. Both the xml files are of same structure. I am trying to update attribute values in the master xml file. But the master file goes blank once, the file is updated. I am not sure where I am wrong.
Below is the xml structure:
<item-descriptor name="user" cache-mode="simple" item-cache-size="3000" query-cache-size="1000" item-cache-timeout="900000" query-expire-timeout="60000" />
<item-descriptor name="contactInfo" cache-mode="simple" item-cache-size="10000" query-cache-size="1000" item-cache-timeout="900000" query-expire-timeout="60000" />
Below is the code for this:
def templatexmlConfig = new XmlParser().parse(templateConfigFile)
def basexmlConfig = new XmlSlurper().parse(baseConfigFile)
def templateItemDesNode = templatexmlConfig.'item-descriptor'
def baseItemDesNode=basexmlConfig.'item-descriptor'
templateItemDesNode.each()
{
Map bindings=[:]
def nameAttr=it.attribute('name')
it.attributes().each{attrName,attrValue->
if(!attrName.equals('name'))
{
bindings.put(attrName,attrValue)
}}
if(baseItemDesNode.find{ it.#name.text().equals(nameAttr)}.size()!=0)
{
bindings.each
{
def a=it.key
def v=it.value
baseItemDesNode.find{ it.#name.text().equals(nameAttr)}.#'a'="${v}" }
}
}
new XmlNodePrinter(new PrintWriter(outputFile)).print(basexmlConfig)

Ok, given two example xml documents:
def templateXml = '''<xml>
| <item-descriptor name="a" cache-mode="r1" item-cache-size="1"/>
| <item-descriptor name="b" cache-mode="r2" item-cache-size="2" new-attr="tim"/>
| <item-descriptor name="z" cache-mode="r3" item-cache-size="3"/>
|</xml>'''.stripMargin() ;
def baseXml = '''<xml>
| <item-descriptor name="b" cache-mode="o1" item-cache-size="10"/>
| <item-descriptor name="c" cache-mode="o2" item-cache-size="11"/>
| <item-descriptor name="a" cache-mode="o3" item-cache-size="12"/>
|</xml>'''.stripMargin()
We can parse these (Using XmlParser for both, you had parser for one, and slurper for the other):
def templatexmlConfig = new XmlParser().parseText( templateXml )
def basexmlConfig = new XmlParser().parseText( baseXml )
And then get the item-descriptor nodes (as you had it)
def templateItemDesNode = templatexmlConfig.'item-descriptor'
def baseItemDesNode = basexmlConfig.'item-descriptor'
Then, loop through the template item-descriptors, generate a map of non-name attributes (using findAll is easier than the loop you had), and replace all nodes on the node in baseXml with the same name:
templateItemDesNode.each() { tidn ->
Map bindings = tidn.attributes().findAll { it.key != 'name' }
def nameAttr = tidn.#name
baseItemDesNode.find { b -> b.#name == nameAttr }?.with { node ->
bindings.each { a, v ->
node.#"$a" = v
}
}
}
And for this easily runnable example, just print out the Xml:
println new StringWriter().with { sw ->
new XmlNodePrinter( new PrintWriter( sw ) ).print(basexmlConfig)
sw.toString()
}
And when run, the above prints out:
<xml>
<item-descriptor name="b" cache-mode="r2" item-cache-size="2" new-attr="tim"/>
<item-descriptor name="c" cache-mode="o2" item-cache-size="11"/>
<item-descriptor name="a" cache-mode="r1" item-cache-size="1"/>
</xml>
Obviously for your working code, you would need to go back to using parse on the XmlParsers to parse your files rather than the Strings I have here, and change the output back to writing to a file...
Hope it helps!

Related

Parsing xml file at build time and modify its values/content

I want to parse a xml file during build time in build.gradle file and want to modify some values of xml, i follow this SO question and answer Load, modify, and write an XML document in Groovy but not able to get any change in my xml file. can anyone help me out. Thanks
code in build.gradle :
def overrideLocalyticsValues(String token) {
def xmlFile = "/path_to_file_saved_in_values/file.xml"
def locXml = new XmlParser().parse(xmlFile)
locXml.resources[0].each {
it.#ll_app_key = "ll_app_key"
it.value = "123456"
}
XmlNodePrinter nodePrinter = new XmlNodePrinter(new PrintWriter(new FileWriter(xmlFile)))
nodePrinter.preserveWhitespace = true
nodePrinter.print(locXml)
}
xml file :
<resources>
<string name="ll_app_key" translatable="false">MY_APP_KEY</string>
<string name="ll_gcm_sender_id" translatable="false">MY_GCM_SENDER_ID</string>
</resources>
In your code : Is it right ...? Where is node name and attribute ..?
locXml.resources[0].each { // Wrongly entered without node name
it.#ll_app_key = "ll_app_key" // Attribute name #name
it.value = "123456" // you can't change or load values here
}
You tried to read and write a same file. Try this code which replaces the exact node of the xml file. XmlSlurper provides this facility.
Updated :
import groovy.util.XmlSlurper
import groovy.xml.XmlUtil
def xmlFile = "test.xml"
def locXml = new XmlSlurper().parse(xmlFile)
locXml.'**'.findAll{ if (it.name() == 'string' && it.#name == "ll_app_key") it.replaceBody 12345 }
new File (xmlFile).text = XmlUtil.serialize(locXml)
Groovy has a better method for this than basic replacement like you're trying to do - the SimpleTemplateEngine
static void main(String[] args) {
def templateEngine = new SimpleTemplateEngine()
def template = '<someXml>${someValue}</someXml>'
def templateArgs = [someValue: "Hello world!"]
println templateEngine.createTemplate(template).make(templateArgs).toString()
}
Output:
<someXml>Hello world!</someXml>

Groovy XMLSlurper Parse Values

I am so close to getting my code completed. I would like to get only the values in an array. Right now I am getting XML declaration plus the line.
Here's my code:
import groovy.xml.XmlUtil
def serverList = new
XmlSlurper().parse("/app/jenkins/jobs/firstsos_servers.xml")
def output = []
serverList.Server.find { it.#name == SERVER}.CleanUp.GZIP.File.each{
output.add(XmlUtil.serialize(it))
}
return output
Here is my XML File:
<ServerList>
<Server name="testserver1">
<CleanUp>
<GZIP>
<File KeepDays="30">log1</File>
<File KeepDays="30">log1.2</File>
</GZIP>
</CleanUp>
</Server>
<Server name="testserver2">
<CleanUp>
<GZIP>
<File KeepDays="30">log2</File>
</GZIP>
</CleanUp>
</Server>
<Server name="testserver3">
<CleanUp>
<GZIP>
<File KeepDays="30">log3</File>
</GZIP>
</CleanUp>
</Server>
When I select testserver1 my output should be:
['log1','log1.2']
What I am getting is this:
<?xml version="1.0" encoding="UTF-8"?><File KeepDays="30">log1</File>
<?xml version="1.0" encoding="UTF-8"?><File KeepDays="30">log2</File>
You need not require to use XmlUtil.serialize()
Here is what you need and following inline comments.
//Define which server you need
def SERVER = 'testserver1'
//Pass the
def serverList = new
XmlSlurper().parse("/app/jenkins/jobs/firstsos_servers.xml")
//Get the filtered file names
def output = serverList.Server.findAll{it.#name == SERVER}.'**'.findAll{it.name() == 'File'}*.text()
println output
return output
Output:
You can quickly try online Demo
def output = []
def node = serverList.Server.find {
it.'name' = 'testserver1'
}.CleanUp.GZIP.File.each {
output.add(it)
}
return output
also there is a copy & paste error in your .xml. You have to add </ServerList> at the end.
`

groovy.lang.MissingMethodException: No signature of method: groovy.util.slurpersupport.NodeChild.add()

I am a newbie to groovy. I'm trying to run this groovy script:
def inxml = "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?> <doc><extension source='ExtractTextStage'>" +
"<field name='DC.Date.Modified'>2006-04-13</field><field name='dc.date'>01-01-2016</field><field name='dc.language'>EN</field></extension>"+
"<extension source='you'>" +
"<field name='dc.date'>02-02-2015</field><field name='dc.language'>EN</field></extension></doc>"
def doc = new XmlSlurper().parseText(inxml)
def date = doc.extension.find{ extension-> extension.#source='ExtractTextStage'}.field.find { field->field.#name == "DC.Date.Modified" }
doc.add("last_modified", date)
print doc​;​
but I'm getting this error:
groovy.lang.MissingMethodException: No signature of method: groovy.util.slurpersupport.NodeChild.add() is applicable for argument types: (java.lang.String, groovy.util.slurpersupport.NodeChild) values: [last_modified, 2006-04-13]
Possible solutions: any(), wait(), name(), pop(), min(), tail()
at Script1.run(Script1.groovy:10)
I want to add a new field named "last_modified" in my XML and assign it the same value as "DC.Date.Modified" field value. Any help would be appreciated.
Think you need to do something like this:
def inxml = '''<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
|<doc>
| <extension source='ExtractTextStage'>
| <field name='DC.Date.Modified'>2006-04-13</field>
| <field name='dc.date'>01-01-2016</field>
| <field name='dc.language'>EN</field>
| </extension>
| <extension source='you'>
| <field name='dc.date'>02-02-2015</field>
| <field name='dc.language'>EN</field>
| </extension>
|</doc>'''.stripMargin()
def doc = new XmlSlurper().parseText(inxml)
def date = doc.extension
.find { extension -> extension.#source='ExtractTextStage'}
.field
.find { field -> field.#name == "DC.Date.Modified" }
def newNode = new XmlSlurper().parseText("<last_modified>$date</last_modified>")
doc.appendNode(newNode)
println groovy.xml.XmlUtil.serialize(doc)

how i use groovy to insert a node which has attribute and value

there is a simple xml, for example:
def rootNode = new XmlSlurper().parseText(
'<root>
<one a1="uno!"/>
<two>Some text!</two>
</root>' )
how can i insert a node
<three type='c'>2334</three>
into root?
i have use this way to insert
rootNode.appendNode{three(type:'c') 2334}
rootNode = new XmlSlurper().parseText(rootNode)
but it return exception.
Below script should give you desired result:
change from yours: three (type:'c', 2334)
import groovy.xml.*
def rootNode = new XmlSlurper().parseText('<root><one a1="uno!"/><two>Some text!</two></root>' )
rootNode.appendNode {
three (type:'c', 2334)
}
def newRootNode = new StreamingMarkupBuilder().bind {mkp.yield rootNode}.toString()
println newRootNode
Output:
<root><one a1='uno!'></one><two>Some text!</two><three type='c'>2334</three></root>

How can I retrieve an element from Groovy's GPathResult using a string that contains a perioid

def elementPath = "elementA.elementB"
xml."${elementPath}".each {}
How to make this work? xml.elementA.elementB.each {} works.
I can think of 2 ways round this...
// Dummy Data for the testing
def CAR_RECORDS = '''
<records>
<car name='HSV Maloo' make='Holden' year='2006'>
<country>Australia</country>
<record type='speed'>Production Pickup Truck with speed of 271kph</record>
</car>
<car name='P50' make='Peel' year='1962'>
<country>Isle of Man</country>
<record type='size'>Smallest Street-Legal Car at 99cm wide and 59 kg in weight</record>
</car>
<car name='Royale' make='Bugatti' year='1931'>
<country>France</country>
<record type='price'>Most Valuable Car at $15 million</record>
</car>
</records>
'''
def xml = new XmlSlurper().parseText( CAR_RECORDS )
def propNames = 'car.country'
// METHOD 1
// Split the propnames into individual properties, then use Inject to walk down this list
// Passing the result to the next property
propNames.split( /\./ ).inject( xml ) { obj, prop -> obj?."$prop" }.each { println it.text() }
// METHOD 2
// Construct a string to evaluate, and pass the xml object to it (it gets substituted in place of x)
Eval.x( xml, "x.${propNames}" ).each { println it.text() }
The javadoc page for Eval.x is here
Hope this helps :-)
xml."elementA.elementB".each {}

Resources