我有一些由脚本生成的 XML,这些 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>
输出应该是这样的:
<customer>
<govId>
<id>@</id>
<idType>SSN</idType>
</govId>
</customer>
我需要删除所有空元素。您会注意到我的代码取出了“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)
有谁知道用 lxml objectify 做这个的方法还是有更简单的方法?如果第二个“govId”元素的所有元素都是空的,我还想完全删除它。
最佳答案
首先,您的代码存在的问题是您迭代的是customers
,而不是govIds
。在第三行,您为每个客户获取 first govId
,并迭代其子项。因此,您需要另一个 for
循环才能使代码按预期运行。
问题末尾的这个小句子使问题变得更加复杂:如果第二个“govId”元素的所有元素都是空的,我还想将其全部删除。
这意味着,除非您只想硬编码检查一层嵌套,否则您需要递归检查元素及其子元素是否为空。例如:
def recursively_empty(e):
if e.text:
return False
return all((recursively_empty(c) for c in e.iterchildren()))
注意:Python 2.5+ 因为使用了 all()
builtin .
然后您可以将代码更改为类似这样的代码,以删除文档中一直为空的所有元素。
# 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)
示例输出:
<customer>
<govId>
<id>@</id>
<idType>SSN</idType>
</govId>
</customer>
您可能想要做的一件事是优化递归函数中的条件 if e.text:
。目前,这会将 None
和空字符串视为空,但不会像空格和换行符那样考虑空白。使用 str.strip()
如果这是您对“空”的定义的一部分。
编辑:正如@Dave 所指出的,可以通过使用 generator expression 来改进递归函数。 :
return all((recursively_empty(c) for c in e.getchildren()))
这不会立即为所有 child 计算 recursively_empty(c)
,而是延迟地为每个 child 计算它。由于 all()
将在第一个 False
元素上停止迭代,这可能意味着显着的性能改进。
编辑 2:可以使用 e.iterchildren()
代替 e.getchildren()
进一步优化表达式。这适用于 lxml etree API和 objectify API .
关于Python lxml - 如何删除空的重复标签,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12694091/