簡體   English   中英

使用elementTree和.remove()刪除XML子元素標記

[英]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似乎非常有幫助,但已達到我被困的程度。 謝謝大家!

問題B:在這種情況下,節點和元素的關系是什么?

我來自有限元素背景,其中節點被理解為元素的一部分,定義了創建元素的部分/角落邊界。 但是,我認為這里的術語使用方式不同,以至於節點不是元素的子集,我是錯誤的嗎? 這兩個術語是否仍然以類似的方式相關?

從樹中刪除元素,無論它在樹中的位置如何,都會因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 ,但庫似乎沒有對nodeelement進行特殊區分。

每個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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM