Really deleting nodes in XMLParser Object Groovy

16,661

Solution 1

Removing nodes works pretty much like other DOM APIs. You have to pass the node you want to delete to the remove method of its parent.

Also the = operator is the assignment operator in Groovy. it.@C1 = 'a' would assign 'a' to the C1 attribute of each B node in the document. Since the result of that assignment is 'a', which is coerced to true by Groovy, find will always return the first node it encounters.

xml=new XmlParser().parseText(x)
def nodeToDel=xml.A.B.C1.find { it.text() == 'a' }
def parent = nodeToDel.parent()
parent.remove(nodeToDel)

Solution 2

Improving solution of Justin Piper, with a fully working example:

def xml = new XmlParser().parseText('''
    <root>
        <element id="10" />
        <element id="20" />
    </root>
''')
def nodeToDel = xml.find { it["@id"] == '20' }

if (nodeToDel) {
    nodeToDel.parent().remove(nodeToDel)
}

println xml
Share:
16,661

Related videos on Youtube

Booyeoo
Author by

Booyeoo

Updated on June 05, 2022

Comments

  • Booyeoo
    Booyeoo almost 2 years

    How to REALLY remove a node via XMLParser:

     x='''<X>
    <A>
     <B c3='1'>
       <C1>a</C1>
       <C2>b</C2>
     </B>
     <B c3='2'>
       <C1>e</C1>
       <C2>e</C2>
     </B>
     <B c3='3'>
       <C1>f</C1>
       <C2>f</C2>
     </B>
    </A>
    </X>
    '''
    
    xml=new XmlParser().parseText(x)
    def nodeToDel=xml.A.B.find{it.@C1='a'}
    xml.remove(nodeToDel)
    println xml
    new XmlNodePrinter(new PrintWriter(new FileWriter(new File('c:/temp/a.xml')))).print(xml)
    

    Seems to work BUT!!!! as i translated this to my problem it still saves the original xml althoguh returning true after running remove-method.

    I googled a little bit and found this BUG. And it seems as i am affected now of that. How can i solve it? Is there a workaround, or do i have to get back to the roots and start to copying it linewise...?? Is groovy really ungroovy here :-/

    edit: As written below, and got experience from that, it is not possible to remove the tag where equals 'e' this way. Only the first Record will be removed. I think there is a problem with the xml format. Not having the needed format:

    <A x='1' y='2'></A>

    and having it in the format

    <A> <x>1</x> <y>2</y> </A>

    Is somebody able to reproduce this bug?

    edit2: I am using the GroovyConsole 1.8.0. Added the c3 attributes to the example. Tried to remove it with same method, same bug: The first B section was removed... Now the most impressing bug: Tried it with other code:

    def xml=new XmlParser().parseText(x)
    xml.A.remove(xml.A.B.find{it.@'c3'= '3'}) //want to remove third section
    new XmlNodePrinter(new PrintWriter(new FileWriter(new File('c:/temp/a.xml')))).print(xml)
    

    results that the first section the property c3 is changed to 3 ?!?!?!?!? wtf...

    I am trying to find a solution for a week now, it is quite exhausting...

    Somebody an idea?

  • Booyeoo
    Booyeoo over 12 years
    This example works only when you want to exculde the scetion with C1='a'. but it doesnt work when you want to remove C1='e' for example... it seems as it only removes the first entry.. I think everybody would assume just change 'a' to 'e' but as you will see it doesnt work... o Please tell me that you also get the false result. i used following code: xml=new XmlParser().parseText(x) def nodeToDel=xml.A.B.find{it.@C1= 'e'} def parent = nodeToDel.parent() parent.remove(nodeToDel) new XmlNodePrinter(new PrintWriter(new FileWriter(new File('c:/temp/a.xml')))).print(xml)
  • Justin Piper
    Justin Piper over 12 years
    I see the problem. I focused on the question of how to actually remove nodes, however the GPath expression was also incorrect and only coincidentally produced the desired result because the node it was looking for happened to be the first one. I've updated my answer. Let me know if that works better.
  • Booyeoo
    Booyeoo over 12 years
    Thanks champ, it works... i already wondered about the = and this also explain why there was a new attribute made in the xml.. and now i got the needed result. The only thing i still dont get, how it works with attributes now: <B c3='3'> i would assume it.@'c3'== '3' but this didnt work, and doesnt work with .text() either
  • Justin Piper
    Justin Piper over 12 years
    If you're doing xml.A.B.C1.find then in the closure it should only be C1 elements. xml.A.B.find { it.@c3 == '3' } should find the first B element with a c3 attribute with a value of 3.