我想删除某个标记值的元素,然后写出 .xml 文件,不包含那些已删除元素的任何标记;是我创建新树的唯一选择吗?
有两个选项可以移除/删除一个元素:
clear() Resets an element. This function removes all subelements, clears all attributes, and sets the text and tail attributes to None.
起初我使用它,它的目的是从元素中删除数据,但我仍然留下一个空元素:
# Remove all elements from the tree that are NOT "job" or "make" or "build" elements
log = open("debug.log", "w")
for el in root.iter(*):
if el.tag != "job" and el.tag != "make" and el.tag != "build":
print("removed = ", el.tag, el.attrib, file=log)
el.clear()
else:
print("NOT", el.tag, el.attrib, file=log)
log.close()
tree.write("make_and_job_tree.xml", short_empty_elements=False)
问题是 xml.etree.ElementTree.ElementTree.write() still writes out empty tags no matter what:
...The keyword-only short_empty_elements parameter controls the formatting of elements that contain no content. If True (the default), they are emitted as a single self-closed tag, otherwise they are emitted as a pair of start/end tags.
为什么没有不打印那些空标签的选项!随便。
然后我想我可以试试
remove(subelement) Removes subelement from the element. Unlike the find* methods this method compares elements based on the instance identity, not on tag value or contents.
但这只对子元素起作用。
所以我必须do something like :
for el in root.iter(*):
for subel in el:
if subel.tag != "make" and subel.tag != "job" and subel.tag != "build":
el.remove(subel)
但是这里有一个大问题:我通过删除元素使迭代器无效,对吧?
通过添加 if subel 来简单地检查元素是否为空就足够了吗?:
if subel and subel.tag != "make" and subel.tag != "job" and subel.tag != "build"
或者我是否必须在每次使它无效时都为树元素获取一个新的迭代器?
记住:我只是想写出没有空元素标签的 xml 文件。
这是一个例子。
<?xml version="1.0"?>
<data>
<country name="Liechtenstein">
<rank>1</rank>
<year>2008</year>
<gdppc>141100</gdppc>
<neighbor name="Austria" direction="E"/>
<neighbor name="Switzerland" direction="W"/>
</country>
<country name="Singapore">
<rank>4</rank>
<year>2011</year>
<gdppc>59900</gdppc>
<neighbor name="Malaysia" direction="N"/>
</country>
<country name="Panama">
<rank>68</rank>
<year>2011</year>
<gdppc>13600</gdppc>
<neighbor name="Costa Rica" direction="W"/>
<neighbor name="Colombia" direction="E"/>
</country>
</data>
假设我想删除对neighbor 的任何提及。 理想情况下,我希望在删除后得到以下输出:
<?xml version="1.0"?>
<data>
<country name="Liechtenstein">
<rank>1</rank>
<year>2008</year>
<gdppc>141100</gdppc>
</country>
<country name="Singapore">
<rank>4</rank>
<year>2011</year>
<gdppc>59900</gdppc>
</country>
<country name="Panama">
<rank>68</rank>
<year>2011</year>
<gdppc>13600</gdppc>
</country>
</data>
问题是,当我使用 clear() 运行代码(参见上面的第一个代码块)并将其写入文件时,我得到了这个:
<data>
<country name="Liechtenstein">
<rank>1</rank>
<year>2008</year>
<gdppc>141100</gdppc>
<neighbor></neighbor><neighbor></neighbor></country>
<country name="Singapore">
<rank>4</rank>
<year>2011</year>
<gdppc>59900</gdppc>
<neighbor></neighbor></country>
<country name="Panama">
<rank>68</rank>
<year>2011</year>
<gdppc>13600</gdppc>
<neighbor></neighbor><neighbor></neighbor></country>
</data>
注意 neighbor 仍然出现。
我知道我可以轻松地在输出上运行正则表达式,但必须有一种方法(或另一个 Python api)可以即时执行此操作,而不是要求我再次触摸我的 .xml 文件.
请您参考如下方法:
import lxml.etree as et
xml = et.parse("test.xml")
for node in xml.xpath("//neighbor"):
node.getparent().remove(node)
xml.write("out.xml",encoding="utf-8",xml_declaration=True)
使用 elementTree,我们需要找到邻居节点的父节点,然后找到该父节点内的邻居节点 并删除它们:
from xml.etree import ElementTree as et
xml = et.parse("test.xml")
for parent in xml.getroot().findall(".//neighbor/.."):
for child in parent.findall("./neighbor"):
parent.remove(child)
xml.write("out.xml",encoding="utf-8",xml_declaration=True)
两者都会给你:
<?xml version='1.0' encoding='utf-8'?>
<data>
<country name="Liechtenstein">
<rank>1</rank>
<year>2008</year>
<gdppc>141100</gdppc>
</country>
<country name="Singapore">
<rank>4</rank>
<year>2011</year>
<gdppc>59900</gdppc>
</country>
<country name="Panama">
<rank>68</rank>
<year>2011</year>
<gdppc>13600</gdppc>
</country>
</data>
使用您的属性逻辑并修改 xml,如下所示:
x = """<?xml version="1.0"?>
<data>
<country name="Liechtenstein">
<rank>1</rank>
<year>2008</year>
<gdppc>141100</gdppc>
<neighbor name="Austria" direction="E"/>
<neighbor name="Switzerland" direction="W"/>
</country>
<country name="Singapore">
<rank>4</rank>
<year>2011</year>
<gdppc>59900</gdppc>
<neighbor name="Costa Rica" direction="W" make="foo" build="bar" job="blah"/>
<neighbor name="Malaysia" direction="N"/>
</country>
<country name="Panama">
<rank>68</rank>
<year>2011</year>
<gdppc>13600</gdppc>
<neighbor name="Costa Rica" direction="W" make="foo" build="bar" job="blah"/>
<neighbor name="Colombia" direction="E"/>
</country>
</data>"""
使用 lxml:
import lxml.etree as et
xml = et.fromstring(x)
for node in xml.xpath("//neighbor[not(@make) and not(@job) and not(@make)]"):
node.getparent().remove(node)
print(et.tostring(xml))
会给你:
<data>
<country name="Liechtenstein">
<rank>1</rank>
<year>2008</year>
<gdppc>141100</gdppc>
</country>
<country name="Singapore">
<rank>4</rank>
<year>2011</year>
<gdppc>59900</gdppc>
<neighbor name="Costa Rica" direction="W" make="foo" build="bar" job="blah"/>
</country>
<country name="Panama">
<rank>68</rank>
<year>2011</year>
<gdppc>13600</gdppc>
<neighbor name="Costa Rica" direction="W" make="foo" build="bar" job="blah"/>
</country>
</data>
ElementTree 中同样的逻辑:
from xml.etree import ElementTree as et
xml = et.parse("test.xml").getroot()
atts = {"build", "job", "make"}
for parent in xml.findall(".//neighbor/.."):
for child in parent.findall(".//neighbor")[:]:
if not atts.issubset(child.attrib):
parent.remove(child)
如果您正在使用 iter:
from xml.etree import ElementTree as et
xml = et.parse("test.xml")
for parent in xml.getroot().iter("*"):
parent[:] = (child for child in parent if child.tag != "neighbor")
你可以看到我们得到了完全相同的输出:
In [30]: !cat /home/padraic/untitled6/test.xml
<?xml version="1.0"?>
<data>
<country name="Liechtenstein">#
<neighbor name="Austria" direction="E"/>
<rank>1</rank>
<neighbor name="Austria" direction="E"/>
<year>2008</year>
<neighbor name="Austria" direction="E"/>
<gdppc>141100</gdppc>
<neighbor name="Austria" direction="E"/>
<neighbor name="Switzerland" direction="W"/>
</country>
<country name="Singapore">
<rank>4</rank>
<year>2011</year>
<gdppc>59900</gdppc>
<neighbor name="Malaysia" direction="N"/>
</country>
<country name="Panama">
<rank>68</rank>
<year>2011</year>
<gdppc>13600</gdppc>
<neighbor name="Costa Rica" direction="W"/>
<neighbor name="Colombia" direction="E"/>
</country>
</data>
In [31]: paste
def test():
import lxml.etree as et
xml = et.parse("/home/padraic/untitled6/test.xml")
for node in xml.xpath("//neighbor"):
node.getparent().remove(node)
a = et.tostring(xml)
from xml.etree import ElementTree as et
xml = et.parse("/home/padraic/untitled6/test.xml")
for parent in xml.getroot().iter("*"):
parent[:] = (child for child in parent if child.tag != "neighbor")
b = et.tostring(xml.getroot())
assert a == b
## -- End pasted text --
In [32]: test()
