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.

Was it helpful?

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
scroll top