[英]Python lxml - How to remove empty repeated tags
我有一些由腳本生成的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
。 在第三行,您為每個客戶采用第一個 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 API和objectify API 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.