我有一些由脚本生成的XML,可能有也可能没有空元素.有人告诉我,现在我们不能在XML中拥有空元素.这是一个例子:
<customer>
<govId>
<id>@</id>
<idType>SSN</idType>
<issueDate/>
<expireDate/>
<dob/>
<state/>
<county/>
<country/>
</govId>
<govId>
<id/>
<idType/>
<issueDate/>
<expireDate/>
<dob/>
<state/>
<county/>
<country/>
</govId>
</customer>
Run Code Online (Sandbox Code Playgroud)
输出应如下所示:
<customer>
<govId>
<id>@</id>
<idType>SSN</idType>
</govId>
</customer>
Run Code Online (Sandbox Code Playgroud)
我需要删除所有空元素.你会注意到我的代码在"govId"子元素中删除了空的东西,但是在第二个元素中没有取出任何东西.我目前正在使用lxml.objectify.
这基本上就是我在做什么:
root = objectify.fromstring(xml)
for customer in root.customers.iterchildren():
for e in customer.govId.iterchildren():
if not e.text:
customer.govId.remove(e)
Run Code Online (Sandbox Code Playgroud)
有没有人知道用lxml objectify做这个的方法还是有一个更简单的方法期?如果它的所有元素都是空的,我还想完整地删除第二个"govId"元素.
Luk*_*raf 12
首先,你的代码的问题是你正在迭代customers,但没有结束govIds.在第三行,您为每个客户选择第 govId一个,并迭代其子项.因此,您需要另一个for循环来使代码按照您的意图工作.
在你的问题结尾处的这个小句子然后使问题变得更复杂:如果所有元素都是空的,我还想完整地删除第二个"govId"元素.
这意味着,除非您想要硬编码只检查一个级别的嵌套,否则需要递归检查元素及其子元素是否为空.像这样例如:
def recursively_empty(e):
if e.text:
return False
return all((recursively_empty(c) for c in e.iterchildren()))
Run Code Online (Sandbox Code Playgroud)
注意:Python 2.5+因为使用了all()内置.
然后,您可以将代码更改为类似的内容,以删除文档中一直空着的所有元素.
# Walk over all elements in the tree and remove all
# nodes that are recursively empty
context = etree.iterwalk(root)
for action, elem in context:
parent = elem.getparent()
if recursively_empty(elem):
parent.remove(elem)
Run Code Online (Sandbox Code Playgroud)
样本输出:
<customer>
<govId>
<id>@</id>
<idType>SSN</idType>
</govId>
</customer>
Run Code Online (Sandbox Code Playgroud)
您可能想要做的一件事是改进if e.text:递归函数中的条件.目前,这将考虑None将空字符串视为空,但不考虑空格和换行符等空格.str.strip()如果这是你的"空"定义的一部分,请使用.
编辑:正如@Dave所指出的,可以通过使用生成器表达式来改进递归函数:
return all((recursively_empty(c) for c in e.getchildren()))
Run Code Online (Sandbox Code Playgroud)
这不会一次评估recursively_empty(c)所有孩子,而是懒洋洋地评估每个孩子.由于all()将停止对第一个False元素的迭代,这可能意味着显着的性能改进.
编辑2:通过使用e.iterchildren()而不是表达式可以进一步优化e.getchildren().这适用于lxml etree API和objectify API.