5

我需要帮助使用 Python 和 elementTree 库调整我的 XML 文件。

对于某些背景,我不是学生,而是在工业界工作。我希望通过使这些更改自动化来为自己节省大量的手动工作,通常我会使用我更熟悉的 C++ 等语言来完成这项工作。然而,在我的小组中使用 Python 是一个推动力,所以我将其用作功能和学习练习。

您能否更正我对术语的使用和理解?我不只是想让代码工作,而是要知道我对它如何工作的理解是正确的。

问题本身:

目标:从 XML 文件中删除子元素“权重”。

使用 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)

我已经阅读了这个有用的资源,但已经到了我被卡住的地步。

第二个问题:在这个上下文中节点和元素的关系是什么?

我来自有限元背景,其中节点被理解为元素的一部分,定义了创建元素的部分/角边界。但是,我认为术语在这里的使用方式不同,因此节点不是元素的子集,我错了吗?这两个术语是否仍然以类似的方式相关?

4

4 回答 4

10

从树中删除一个元素,无论它在树中的位置如何,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 文档或多或少地使用“节点”与“元素”互换。更具体地说,它似乎使用“节点”一词来指代“元素”类型的 Python 对象或此类对象所指的 XML 元素。

于 2016-05-20T00:25:10.937 回答
5

您的问题是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)列出特定元素的所有子元素。然后,您过滤掉具有名称(标签)的元素,weight因此同时具有对父子和子的引用,因此现在可以删除一个元素。

尽管您只能在XML 上下文中找到该术语,但Library 似乎并没有特别区分node和。elementelement

每个 XML 文档都具有逻辑结构和物理结构。在物理上,文档由称为实体的单元组成。一个实体可以引用其他实体以将它们包含在文档中。文档从“根”或文档实体开始。从逻辑上讲,文档由声明、元素、注释、字符引用和处理指令组成,所有这些都在文档中通过显式标记来表示。逻辑和物理结构必须正确嵌套,如 4.3.2 格式良好的已解析实体中所述。

于 2016-05-20T00:42:56.557 回答
2

如果你知道你只有一个权重标签的实例,你可以避免循环的痛苦,只需找到父元素和子元素,然后删除子元素,例如:

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)

于 2019-02-07T11:40:22.660 回答
1

要在您不断增长的词汇中再添加一个术语,请考虑XSLT,这是一种特殊用途的声明性语言,旨在转换 XML 文档以满足各种最终使用需求。事实上,XSLT 是一个格式良好的 XML 文件,带有脚本指令!虽然 Python 的内置xml.etree没有 XSLT 处理器,但外部lxml(基于libxslt)模块维护一个 XSLT 1.0 处理器。更重要的是,XSLT 是可移植的,可以被其他语言(Java、PHP、Perl、VB 甚至 C++)甚至专用的可执行文件(Saxon、Xalan)和命令行解释器(Bash、PowerShell)使用。

您会在下面注意到,没有使用一个循环。在 XSLT 脚本中,身份转换按原样复制整个文档,并且匹配到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"))
于 2016-05-20T02:35:34.097 回答