Search code examples
xmlgroovysoapuixmlslurper

Get all elements between 2 elements including other elements, values and attributes in Groovy


I have an XML document that looks like this

<home>
    <address>
        <address1 city="asd">
            <Language spoken="English">
                <SubLanguages>true</SubLanguages>
            </Language>
        </address1>
        <address1 city="asd1">
            <Language spoken="Spanish"/>
        </address1>
        <address1 city="asd">
            <Language spoken="Hebrew"/>
        </address1>
    </address>
</home>

As you can see, the address1 city="asd" tag appears twice in the XML. I know the ideal way is to have a single address1 element with both the Language elements under it, but unfortunately, that is not possible right now.

What I am looking for is a way that will identify all address1 tags with city="asd" and THEN copy all the child elements below it into a string. So basically, what I am looking for is a String that will look like

<address1 city="asd">
    <Language spoken="English">
        <SubLanguages>true</SubLanguages>
    </Language>
    <Language spoken="Hebrew"/>
</address1>

(the ideal way of representing this XML)

Right now, I am using XMLSlurper to parse through the XML and using findAll to identify all the nodes where the value of city tag in address1 is "asd". Where I am stuck is how to copy the rest of the sub-elements all inside of each tag as Strings

This is my first question on SO, so I apologize in advance if I am not clear enough / have missed out on some proper formattings / styles! Thanks for understanding!


Solution

  • You can use StreamingMarkupBuilder to insert the children into a new document (assuming your existing xml is in a variable called xml):

    import groovy.xml.*
    
    def home = new XmlSlurper().parseText(xml)
    
    def newXml = new StreamingMarkupBuilder().bind {
        address1(city: 'asd') {
            home.address.address1
                .findAll { it.@city.text() == 'asd' }
                .each { mkp.yield it.children() }
        }
    }.toString()