使用python删除没有子节点的xml节点

时间:2017-09-19 11:35:59

标签: python xml parsing

我有以下xml输出:

<?xml version='1.0' encoding='ISO-8859-1'?>
<?xml-stylesheet type='text/xsl' href='image_metadata_stylesheet.xsl'?>
<dataset>
<images>
  <image file='VideoExtract/testset/10224.jpg'>
    <box top='436' left='266' width='106' height='61'>
      <label>1</label>
    </box>
  </image>
  <image file='VideoExtract/testset/1044.jpg'>
    <box top='507' left='330' width='52' height='27'>
      <label>2</label>
    </box>
  </image>
  <image file='VideoExtract/testset/10675.jpg'>
  </image>
</images>
</dataset>

由此,我想删除所有没有子节点的节点。例如,图像内的第三个图像节点没有子节点。如何删除此子节点。期望的输出将是

<?xml version='1.0' encoding='ISO-8859-1'?>
<?xml-stylesheet type='text/xsl' href='image_metadata_stylesheet.xsl'?>
<dataset>
<images>
  <image file='VideoExtract/testset/10224.jpg'>
    <box top='436' left='266' width='106' height='61'>
      <label>1</label>
    </box>
  </image>
  <image file='VideoExtract/testset/1044.jpg'>
    <box top='507' left='330' width='52' height='27'>
      <label>2</label>
    </box>
  </image>
</images>
</dataset>

我尝试了以下方法,但它没有帮助。

from lxml import etree as ET
root = ET.parse('testxml.xml')
for child in root.iterfind('targetElement'):
    if(len(child.attrib) < 1 and len(child) < 1):
        child.getparent().remove(child)

2 个答案:

答案 0 :(得分:2)

由于您使用lxml模块,请考虑XSLT,这是专门用于转换XML文件的专用语言。使用这种方法,不需要for循环或if逻辑。

实际上,您的XML看起来按照处理指令使用XSLT,因此您可以在该样式表中包含以下脚本。以下脚本在任何<image>标记上运行Identity Transform和一个空模板,其中包含零个子项。空模板会删除此类节点。

XSLT (另存为.xsl文件)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:strip-space elements="*"/>
  <xsl:output indent="yes"/>

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="image[count(*)=0]"/>

</xsl:stylesheet>

<强>的Python

import lxml.etree as et

doc = et.parse('Input.xml')
xsl = et.parse('XSLT_Script.xsl')

transform = et.XSLT(xsl)    
result = transform(doc)

# OUTPUT TO SCREEN
print(result)

# OUTPUT TO FILE
with open('Output.xml', 'wb') as f:
    f.write(result)

<强>输出

<?xml version="1.0"?>
<?xml-stylesheet type='text/xsl' href='image_metadata_stylesheet.xsl'?><dataset>
  <images>
    <image file="VideoExtract/testset/10224.jpg">
      <box top="436" left="266" width="106" height="61">
        <label>1</label>
      </box>
    </image>
    <image file="VideoExtract/testset/1044.jpg">
      <box top="507" left="330" width="52" height="27">
        <label>2</label>
      </box>
    </image>
  </images>
</dataset>

答案 1 :(得分:1)

此代码可能完全符合您在问题中提出的要求。我怀疑这正是你想要的。

>>> from lxml import etree
>>> tree = etree.parse('testxml.xml')
>>> for el in tree.iter():
...     el.tag, len(list(el.iterchildren()))
...     if not len(list(el.iterchildren())):
...         parent = el.getparent()
...         if parent is not None:
...             parent.remove(el)
...             
('dataset', 1)
('images', 3)
('image', 1)
('box', 1)
('label', 0)
('image', 1)
('box', 1)
('label', 0)
('image', 0)
>>> tree.write('temp.xml', pretty_print=True)

这是生成的xml文件。

<?xml-stylesheet type='text/xsl' href='image_metadata_stylesheet.xsl'?>
<dataset>
<images>
  <image file="VideoExtract/testset/10224.jpg">
    <box top="436" left="266" width="106" height="61">
      </box>
  </image>
  <image file="VideoExtract/testset/1044.jpg">
    <box top="507" left="330" width="52" height="27">
      </box>
  </image>
  </images>
</dataset>

我注意到label个节点不包含任何节点(尽管它们包含文本!);因此,输出中缺少它们。 这是你真正想要的吗?

相比之下,此版本的代码会保留label元素。

>>> tree = etree.parse('testxml.xml')
>>> for el in tree.iter():
...     if len(list(el.iterchildren())) or ''.join([_.strip() for _ in el.itertext()]):
...         pass
...     else:
...         parent = el.getparent()
...         if parent is not None:
...             parent.remove(el)

这是这种情况下的结果文件。

<?xml-stylesheet type='text/xsl' href='image_metadata_stylesheet.xsl'?>
<dataset>
<images>
  <image file="VideoExtract/testset/10224.jpg">
    <box top="436" left="266" width="106" height="61">
      <label>1</label>
    </box>
  </image>
  <image file="VideoExtract/testset/1044.jpg">
    <box top="507" left="330" width="52" height="27">
      <label>2</label>
    </box>
  </image>
  </images>
</dataset>