Deleting nodes in XML using Xpath in Groovy
Question
I've got this Groovy code working to delete nodes using xpath strings, but I'm having problems deleting nodes where the xpath results in multiple node instances.
Sample XML...
<root>
<element1>foo</element1>
<element2>bar</element2>
<items>
<item>
<name>a</name>
<desc>b</desc>
<item>
<item>
<name>c</name>
<desc>x</desc>
</item>
</items>
</root>
Code to delete nodes...
def resource = XmlSlurper().parseText(xml)
def xpathsToDelete = ['/root/element1','/root/items/item/name']
xpathsToDelete.each {
def pathTokens = it.path.tokenize '/'
def currentNode = resource
if ( currentNode.name() == pathTokens.first() ) {
def xpath = pathTokens.tail().join '/'
currentNode = currentNode."${xpath}"
currentNode.replaceNode{}
}
}
The above code removes the node element1
using xpath /root/element1
, which evaluates to a single node, but does not work for /root/items/name
which evaluates to multiple nodes.
Solution
This is a tricky one. It is related to this question, which is vital to my answer.
Here is a solution:
import groovy.util.*
import groovy.xml.*
def xml = """<root>
<element1>foo</element1>
<element2>bar</element2>
<items>
<item>
<name>a</name>
<desc>b</desc>
</item>
<item>
<name>c</name>
<desc>x</desc>
</item>
</items>
</root>"""
def removeNodes = { doc, path ->
def nodes = doc
path.split("\\.").each { nodes = nodes."${it}" }
nodes.each { it.replaceNode{} }
}
def resource = new XmlSlurper().parseText(xml)
def xpathsToDelete = ['/root/element1','/root/items/item/name']
xpathsToDelete.each { xpath ->
def trimXPath = xpath.replaceFirst( "/root/", "").replace("/",".")
removeNodes(resource, trimXPath)
}
println XmlUtil.serialize(new StreamingMarkupBuilder().bind {
mkp.yield resource
})
OTHER TIPS
This seems to work as well:
import groovy.xml.*
def xml = '''<root>
| <element1>foo</element1>
| <element2>bar</element2>
| <items>
| <item>
| <name>a</name>
| <desc>b</desc>
| </item>
| <item>
| <name>c</name>
| <desc>x</desc>
| </item>
| </items>
|</root>'''.stripMargin()
def newxml = new XmlSlurper().parseText( xml ).with { x ->
[ '/root/element1', '/root/items/item/name' ].each { path ->
def s = path.split( '/' ).drop( 2 ).inject( x ) { element, p ->
element."$p"
}?.replaceNode {}
}
x
}
println XmlUtil.serialize(new StreamingMarkupBuilder().bind {
mkp.yield newxml
})
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow