7

我有一些由可能有也可能没有空元素的脚本生成的 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”元素。

4

1 回答 1

15

首先,您的代码的问题是您正在迭代customers,但没有结束govIds。在第三行,您为每个客户获取 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()内置的.

然后,您可以将代码更改为类似这样的内容,以删除文档中一直为空的所有元素。

# 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 所指出的,递归函数可以通过使用生成器表达式来改进:

return all((recursively_empty(c) for c in e.getchildren()))

这不会recursively_empty(c)一次评估所有孩子,而是懒惰地评估每个孩子。由于all()将在第一个False元素上停止迭代,这可能意味着显着的性能改进。

编辑 2:可以通过使用e.iterchildren()而不是进一步优化表达式e.getchildren()。这适用于lxml etree APIobjectify API

于 2012-10-02T16:52:15.637 回答