Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Groovy: how to insert a node at the beginning of a list of elements in XML read in using XMLSlurper()

Tags:

xml

groovy

I'm probably missing something obvious, as I'm a noob with Groovy, but I've searched but haven't found quite what I'm looking for. I have a test class where I'm reading in some XML; I want to insert an element at the beginning of a series of elements. I have figured out how to replace the first element, and I've figured out how to append a node to the end of the list, but I can't seem to grok how to insert an element at the beginning of the list (or ideally, any arbitrary position).

For example:

@Test
void foo()
{
    def xml = """<container>
                   <listofthings>
                       <thing id="100" name="foo"/>
                   </listofthings>
                 </container>"""

    def root = new XmlSlurper().parseText(xml)
    root.listofthings.thing[0].replaceNode ( { thing(id:101, name:'bar') })
    root.listofthings.appendNode  ( { thing(id:102, name:'baz') })

    def outputBuilder = new groovy.xml.StreamingMarkupBuilder()
    String result = outputBuilder.bind { mkp.yield root }
    print result
}

which yields:

<container>
   <listofthings>
     <thing id='101' name='bar'/>
     <thing id='102' name='baz'/>
   </listofthings>
</container>

What I really want is to insert a node at the beginning of listofthings, i.e. something to replace the call to replaceNode that would instead insert the thing with id 101 before the thing with id 100. I would also be nice if say, I had a longer list, to insert a node after the n'th element.

(Incidentally, is there a way to get the output in a more readable format? The output from StreamingMarkupBuilder all ends up as a single line of text; I reformatted it for clarity above)

Edit: I'm using 1.7.5, that's bundled with Eclipse, if it matters.

like image 845
Eric Asberry Avatar asked Jun 29 '11 19:06

Eric Asberry


People also ask

What is groovy XmlSlurper?

Groovy's internal XmlParser and XmlSlurper provide access to XML documents in a Groovy-friendly way that supports GPath expressions for working on the document. XmlParser provides an in-memory representation for in-place manipulation of nodes, whereas XmlSlurper is able to work in a more streamlike fashion.

What is node in groovy script?

Represents an arbitrary tree node which can be used for structured metadata or any arbitrary XML-like tree. A node can have a name, a value and an optional Map of attributes.

What is GPathResult?

GPathResult, which is a wrapper class for Node. GPathResult provides simplified definitions of methods such as: equals() and toString() by wrapping Node#text().


1 Answers

One way you can do this by extracting the thing elements out of your original xml into a list, manipulating the list, then rebuilding the document with this new list:

// function to take a single line xml output, and make it pretty
String renderFormattedXml( String xml ){
  def stringWriter = new StringWriter()
  def node = new XmlParser().parseText( xml )
  new XmlNodePrinter( new PrintWriter( stringWriter ) ).print( node )
  stringWriter.toString()
}

def xml = """<container>
               <listofthings>
                   <thing id="100" name="foo"/>
               </listofthings>
             </container>"""

def root = new XmlSlurper().parseText(xml)

def things = root.listofthings*.thing

// Insert one at pos 0
things.add( 0, { thing( id:98, name:'tim' ) } )

// And one at the end
things.add( { thing( id:999, name:'zebra' ) } )

// And one at position 1
things.add( 1, { thing( id:99, name:'groovy' ) } )

def outputBuilder = new groovy.xml.StreamingMarkupBuilder()
String result = outputBuilder.bind {
  container {
    listofthings {
      mkp.yield things
    }
  }
}
println renderFormattedXml( result )

which prints

<container>
  <listofthings>
    <thing id="98" name="tim"/>
    <thing id="99" name="groovy"/>
    <thing id="100" name="foo"/>
    <thing id="999" name="zebra"/>
  </listofthings>
</container>
like image 163
tim_yates Avatar answered Oct 05 '22 01:10

tim_yates