1

I need to remove an XML element while preserving its data. The lxml function strip_tags does remove the elements, but it works recursively and I want to strip a single element.

I tried using the answer on this post, but remove removes the whole element.

xml="""
<groceries>
  One <fruit state="rotten">apple</fruit> a day keeps the doctor away.
  This <fruit state="fresh">pear</fruit> is fresh.
</groceries>
"""

tree=ET.fromstring(xml)

for bad in tree.xpath("//fruit[@state='rotten']"):
    bad.getparent().remove(bad)

print (ET.tostring(tree, pretty_print=True))

I want to get

<groceries>
    One apple a day keeps the doctor away.
    This <fruit state="fresh">pear</fruit> is fresh.
</groceries>\n'

I get

<groceries>
    This <fruit state="fresh">pear</fruit> is fresh.
</groceries>\n'

I tried using strip_tags:

for bad in tree.xpath("//fruit[@state='rotten']"):
    ET.strip_tags(bad.getparent(), bad.tag)

<groceries>
    One apple a day keeps the doctor away.
    This pear is fresh.
</groceries>

But that strips everything, and I just want to strip the element with the state='rotten'.

Progman
  • 16,827
  • 6
  • 33
  • 48
Leonardo
  • 1,533
  • 17
  • 28

1 Answers1

1

Maybe someone else has a better idea, but this is a possible workaround:

bad = tree.xpath(".//fruit[@state='rotten']")[0] #for simplicity, I didn't bother with a for loop in this case
txt = bad.text+bad.tail # collect the text content of bad; strangely enough it's not just 'apple'
bad.getparent().text += txt # add the collected text to the parent's existing text
tree.remove(bad) # this gets rid only of this specific 'bad'
print(etree.tostring(tree).decode())

Output:

<groceries>
  One apple a day keeps the doctor away.
  This <fruit state="fresh">pear</fruit> is fresh.
</groceries>
Jack Fleeting
  • 24,385
  • 6
  • 23
  • 45