您好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似乎很有帮助,但已达到我被卡住的程度。提前谢谢大家!
我来自有限元素背景,其中节点被理解为元素的一部分,定义了创建元素的部分/角落边界。但是,我认为这里的术语使用方式不同,以至于节点不是元素的子集,我是错误的吗?这两个术语是否仍然以类似的方式相关?
答案 0 :(得分:5)
从树中删除元素,无论它在树中的位置如何,都会因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)
至于你的第二个问题,the ElementTree documentation或多或少地使用“节点”与“元素”互换。更具体地说,似乎使用“node”一词来指代“Element”类型的Python对象或这样一个对象所引用的XML元素。
答案 1 :(得分:1)
您的问题是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
的元素,从而引用父元素和子元素,因此现在可以删除元素。
图书馆似乎没有特别区分node
和element
,尽管您只能在XML context中找到element
这个词。
每个XML文档都具有逻辑和物理结构。 在物理上,文档由称为实体的单元组成。一个 实体可以指其他实体将其纳入 文献。文档以“根”或文档实体开头。从逻辑上讲, 该文件由声明,元素,评论, 字符引用和处理指令,所有这些都是 通过显式标记在文档中指出。逻辑和物理 结构必须正确嵌套,如4.3.2 Well-Formed中所述 解析实体。
答案 2 :(得分:0)
要在不断增长的词汇表中再添加一个术语,请考虑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"))
答案 3 :(得分:0)
如果您知道您只有一个weight标签实例,则可以避免循环的麻烦,只需查找父元素和子元素,然后删除该子元素即可,例如:
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)