本文最后更新于:几秒前
XML作为一种常见的数据交换格式,在数据交换中扮演了重要的角色,但是开放的数据格式定义也为XML的解析带来了一定的不确定性,我在实际的工作里遇到了一定的小困难,在此记录以备查阅。
本文XML的限定
在开始XML
解析相关方法之前,我想对本文所提及的XML做一个简单限定。XML(Extensible Markup Language)中文全称可拓展标记语言。其变体在众多领域都有应用,如:数据交换、页面显示等。广为人知的HTML
就是一种符合XML
标准的语言。本文仅讨论用于数据交换的XML
,其格式定义为<key>value</key>
或<key attribute="value1">value</key>
此类的开闭代码块以并联和串联的方式所构建的文本记录,不用于数据展示。常见于图片标记或是序列化数据标记,依据组合方式和key-value组合,可以完成多种复杂数据的定义。例如下面的文本:
1 2 3 4 5 6 7 8 9 <?xml version='1.0' encoding='utf-8'?> <tagDocument > <numberOfFrames id ="2" > 1</numberOfFrames > <frame > <PositionIndex > 1575</PositionIndex > <tool > 208 268 64.844827 5.848515</tool > <tool > 123 174 43.747142 4.022589</tool > </frame > </tagDocument >
Python中解析XML
在python
中,XML
的解析实现的方式有很多,但是综合我的实践结果。我对这些方法做出以下取舍:
1. xml.dom.*
xml.dom.*
包是一个符合W3C关于XML定义的DOM API,它实现的过程是首先将整个文档视为一个树,通过依次访问相邻节点和子节点的方式实现数据解析的。上述XML我们的解析代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 def get_xml_data (filename='test.xml' ): dom_tree = xdom.parse(filename) collection = dom_tree.documentElement nums_frame = collection.getElementsByTagName('numberOfFrames' )[0 ].firstChild.data id_s=collection.getElementsByTagName('numberOfFrames' )[0 ].getAttribute("id" ) frame_nodes = collection.getElementsByTagName('frame' ) pos_dict = {} for node in frame_nodes: frame_name =node.getElementsByTagName('PositionIndex' )[ 0 ].firstChild.data tools = node.getElementsByTagName('tool' ) tool_array = [] for tool in tools: tool_pos = tool.firstChild.data tool_array.append(tool_pos) pos_dict[frame_name] = tool_array return nums_frame, pos_dict
这样的方式在实现时可以提高你对代码的可控性,对于有一定格式的数据而言,可以通过这样的方式来实现,但相应的缺点便是其效率比较低。
2. xml.sax.*
xml.sax.*
是比xml.dom.*
更加低层级的实现,以事件驱动的方式带来了更高的实现效率。但是也带了编写代码的不便性,同样以前文的xml为例,我们的解析代码为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import xml.sax.handlerclass DataHandler (xml.sax.ContentHandler): def __init__ (self ): self.id_s = "" self.frames = "" self.tool_array = [] self.index_array = [] def startElement (self, tag, attrs ): self.CurrentData = tag if tag == 'numberOfFrames' : self.id_s = attrs['id' ] def endElement (self, tag ): if self.CurrentData == "numberOfFrames" : self.frames = self.numberOfFrames elif self.CurrentData == "PositionIndex" : self.index_array.append(self.PositionIndex) elif self.CurrentData == "tool" : if not self.tool.isspace(): self.tool_array.append(self.tool) def characters (self, content ): if self.CurrentData == "PositionIndex" : self.PositionIndex = content elif self.CurrentData == "numberOfFrames" : self.numberOfFrames = content elif self.CurrentData == "tool" : self.tool = content parser = xml.sax.make_parser() parser.setFeature(xml.sax.handler.feature_namespaces, 0 ) handler = DataHandler() parser.setContentHandler(handler) parser.parse("test.xml" )print (handler.tool_array, handler.index_array)
可以看出,这个实现方式非常的复杂,那有没有效率与代码兼顾的呢?
3. xml.etree.ElementTree
xml.etree.ElementTree
是一个比SAX
层级高,比DOM
效率高的解析包,相对于前两者而言,ElementTree
有着更加高效的处理效率和简洁的接口,同时还有对应的C实现:xml.etree.cElementTree
,工程中,我更推荐这个方法,同样对于前文的方法,我们有:
1 2 3 4 5 6 7 8 9 10 11 12 import xml.etree.cElementTree as ET tree = ET.ElementTree(file='test.xml' ) root = tree.getroot() frames = root.iter (tag="numberOfFrames" ) pos_index_array=[] pos_array=[]for children in root.iter (tag="frame" ): for child in children.iter (tag="PositionIndex" ): pos_index_array.append(child.text) for child in children.iter (tag="tool" ): pos_array.append(child.text)print (pos_index_array,pos_array)
效率与代码整洁度都有保证
Java中解析XML
C++中解析XML