[英]Removing XML subelement tags with Python using elementTree and .remove()
您好Stackoverflow社區,
我很感激使用Python和elementTree庫調整XML文件的一些指導。
在某些背景下,我不是學生並且在工業界工作。 我希望通過自動化這些更改來節省大量的手動工作,通常我會用C ++這樣的語言完成這項工作,我更熟悉。 但是,在我的小組中使用Python是一種推動,因此我將其用作功能和學習練習。
因此,解決方案指導是有幫助的,但如有可能,請您更正我對術語和理解的使用? 我不僅僅想讓代碼工作,而是要知道我對其工作方式的理解是正確的。
目標:從XML文件中刪除子元素“weight”。
使用xml代碼(我們只是說它叫做“example.xml”):
<XML_level_1 created="2014-08-19 16:55:02" userID="User@company">
<XML_level_2 manufacturer="company" number="store-25235">
<padUnits value="mm" />
<partDescription value="Part description explained here" />
<weight value="5.2" />
</XML_level_2>
</XML_level_1>
到目前為止,我有以下代碼:
from xml.etree import ElementTree
current_xml_tree = ElementTree.parse(filename_path) # Path to example.xml
current_xml_root = current_xml_tree.getroot()
current_xml_level_2_node = current_xml_root.findall('XML_level_2')
# Extract "weight" value for later use
for weight_value_elem in current_xml_root.iter('weight'):
weight_value = weight_value_elem.get('value')
# Remove weight sub-element from XML
# -------------------------------------
# Get all nodes entitled 'weight' from element
weight_nodes = current_xml_root.findall('weight')
print weight_nodes # result is an empty list
print weight_value_elem # Location of element 'weight' is listed
for weight_node_loc in current_xml_tree.iter('weight'):
print "for-loop check : loop has been entered"
current_xml_tree.getroot().remove(weight_value_elem)
print "for-loop has been processed"
print "Weight line removed from ", filename_path
# Write changes to XML File:
current_xml_tree.write(filename_path)
我看過很多頁面,但是這一頁: http : //www.cmi.ac.in/~madhavan/courses/prog2-2015/docs/python-3.4.2-docs-html/library/xml.etree。 elementtree.html似乎非常有幫助,但已達到我被困的程度。 謝謝大家!
我來自有限元素背景,其中節點被理解為元素的一部分,定義了創建元素的部分/角落邊界。 但是,我認為這里的術語使用方式不同,以至於節點不是元素的子集,我是錯誤的嗎? 這兩個術語是否仍然以類似的方式相關?
從樹中刪除元素,無論它在樹中的位置如何,都會因ElementTree API而不必要地復雜化。 具體來說,沒有元素知道自己的父母,所以我們必須“手工”發現這種關系。
from xml.etree import ElementTree
XML = '''
<XML_level_1 created="2014-08-19 16:55:02" userID="User@company">
<XML_level_2 manufacturer="company" number="store-25235">
<padUnits value="mm" />
<partDescription value="Part description explained here" />
<weight value="5.2" />
</XML_level_2>
</XML_level_1>
'''
# parse the XML into a tree
root = ElementTree.XML(XML)
# Alternatively, parse the XML that lives in 'filename_path'
# tree = ElementTree.parse(filename_path)
# root = tree.getroot()
# Find the parent element of each "weight" element, using XPATH
for parent in root.findall('.//weight/..'):
# Find each weight element
for element in parent.findall('weight'):
# Remove the weight element from its parent element
parent.remove(element)
print ElementTree.tostring(root)
如果你可以切換到lxml
,循環稍微麻煩一點:
for weight in tree.findall("//weight"):
weight.getparent().remove(weight)
至於你的第二個問題, ElementTree文檔使用“node”或多或少可互換地使用“element”。 更具體地說,似乎使用“node”一詞來指代“Element”類型的Python對象或這種對象所引用的XML元素。
您的問題是node.remove()
僅刪除node
直接子元素。 在XML文件您發布的weight
元素是沒有直接的子元素XML_level_1
而是直接子元素XML_level_2
。 另外, ElementTree
的實現方式似乎沒有從子節點到其父節點的鏈接。
您可以按如下方式更改代碼:
from xml.etree import ElementTree
xml_str = '''
<XML_level_1 created="2014-08-19 16:55:02" userID="User@company">
<XML_level_2 manufacturer="company" number="store-25235">
<padUnits value="mm" />
<partDescription value="Part description explained here" />
<weight value="5.2" />
</XML_level_2>
</XML_level_1>
'''
root = ElementTree.fromstring(xml_str)
for elem in root.iter():
for child in list(elem):
if child.tag == 'weight':
elem.remove(child)
說明: root.iter()
以深度優先順序遍歷整個樹, list(elem)
列出特定元素的所有子元素。 然后,您使用name(tag) weight
過濾掉元素,從而引用父元素和子元素,從而現在可以刪除元素。
盡管您只在XML上下文中找到術語element
,但庫似乎沒有對node
和element
進行特殊區分。
每個XML文檔都具有邏輯和物理結構。 在物理上,文檔由稱為實體的單元組成。 實體可以引用其他實體以使其包含在文檔中。 文檔以“根”或文檔實體開頭。 邏輯上,文檔由聲明, 元素 ,注釋,字符引用和處理指令組成,所有這些都通過顯式標記在文檔中指出。 邏輯和物理結構必須正確嵌套,如4.3.2良好形成的解析實體中所述。
要在不斷增長的詞匯表中再添加一個術語,請考慮XSLT ,這是一種專用的聲明性語言,旨在轉換XML文檔以滿足各種最終用途需求。 事實上,XSLT是一個結構良好的XML文件,帶有腳本指令! 雖然Python的內置xml.etree
沒有XSLT處理器,但外部lxml (基於libxslt )模塊維護着一個XSLT 1.0處理器。 更重要的是,XSLT是可移植的,可以被其他語言(Java,PHP,Perl,VB,甚至C ++)甚至專用可執行文件( Saxon,Xalan )和命令行解釋器(Bash,PowerShell)使用。
您將在下面注意到,不使用一個循環。 在XSLT腳本中,Identity Transform按原樣復制整個文檔,空模板匹配weight
(無論它位於何處)將其刪除。
import lxml.etree as ET
xml_str = '''
<XML_level_1 created="2014-08-19 16:55:02" userID="User@company">
<XML_level_2 manufacturer="company" number="store-25235">
<padUnits value="mm" />
<partDescription value="Part description explained here" />
<weight value="5.2" />
</XML_level_2>
</XML_level_1>
'''
dom = ET.fromstring(xml_str)
xslt_str = '''
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<!-- Identity Transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<!-- Empty Template -->
<xsl:template match="weight"/>
</xsl:transform>
'''
xslt = ET.fromstring(xslt_str)
transform = ET.XSLT(xslt) # INITIALIZES TRANSFORMER
newdom = transform(dom) # RUNS TRANSFORMATION ON SOURCE XML
tree_out = ET.tostring(newdom, pretty_print=True) # CONVERTS TREE OBJECT TO STRING
print(tree_out.decode("utf-8"))
如果你知道你只有一個權重標記的實例,你可以避免循環的痛苦,只需找到父元素和子元素,然后刪除子元素,例如:
xml_root = ElementTree.parse(filename_path).getroot() # Path to example.xml
parent_element = xml_root.find('./XML_level_2')
weight_element = xml_root.find('./XML_level_2/weight')
parent_element.remove(weight_element)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.