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.
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
})