Python中将字典转换为XML以及相关的命名空间解析


Posted in Python onOctober 15, 2015

尽管 xml.etree.ElementTree 库通常用来做解析工作,其实它也可以创建XML文档。 例如,考虑如下这个函数:

from xml.etree.ElementTree import Element

def dict_to_xml(tag, d):
'''
Turn a simple dict of key/value pairs into XML
'''
elem = Element(tag)
for key, val in d.items():
  child = Element(key)
  child.text = str(val)
  elem.append(child)
return elem

下面是一个使用例子:

>>> s = { 'name': 'GOOG', 'shares': 100, 'price':490.1 }
>>> e = dict_to_xml('stock', s)
>>> e
<Element 'stock' at 0x1004b64c8>
>>>

转换结果是一个 Element 实例。对于I/O操作,使用 xml.etree.ElementTree 中的 tostring() 函数很容易就能将它转换成一个字节字符串。例如:

>>> from xml.etree.ElementTree import tostring
>>> tostring(e)
b'<stock><price>490.1</price><shares>100</shares><name>GOOG</name></stock>'
>>>

如果你想给某个元素添加属性值,可以使用 set() 方法:

>>> e.set('_id','1234')
>>> tostring(e)
b'<stock _id="1234"><price>490.1</price><shares>100</shares><name>GOOG</name>
</stock>'
>>>

如果你还想保持元素的顺序,可以考虑构造一个 OrderedDict 来代替一个普通的字典。当创建XML的时候,你被限制只能构造字符串类型的值。例如:

def dict_to_xml_str(tag, d):
  '''
  Turn a simple dict of key/value pairs into XML
  '''
  parts = ['<{}>'.format(tag)]
  for key, val in d.items():
    parts.append('<{0}>{1}</{0}>'.format(key,val))
  parts.append('</{}>'.format(tag))
  return ''.join(parts)

问题是如果你手动的去构造的时候可能会碰到一些麻烦。例如,当字典的值中包含一些特殊字符的时候会怎样呢?

>>> d = { 'name' : '<spam>' }

>>> # String creation
>>> dict_to_xml_str('item',d)
'<item><name><spam></name></item>'

>>> # Proper XML creation
>>> e = dict_to_xml('item',d)
>>> tostring(e)
b'<item><name><spam></name></item>'
>>>

注意到程序的后面那个例子中,字符 ‘<' 和 ‘>' 被替换成了 < 和 >

下面仅供参考,如果你需要手动去转换这些字符, 可以使用 xml.sax.saxutils 中的 escape() 和 unescape() 函数。例如:

>>> from xml.sax.saxutils import escape, unescape
>>> escape('<spam>')
'<spam>'
>>> unescape(_)
'<spam>'
>>>

除了能创建正确的输出外,还有另外一个原因推荐你创建 Element 实例而不是字符串, 那就是使用字符串组合构造一个更大的文档并不是那么容易。 而 Element 实例可以不用考虑解析XML文本的情况下通过多种方式被处理。 也就是说,你可以在一个高级数据结构上完成你所有的操作,并在最后以字符串的形式将其输出。

利用命名空间解析XML文档
如果你解析这个文档并执行普通的查询,你会发现这个并不是那么容易,因为所有步骤都变得相当的繁琐。

>>> # Some queries that work
>>> doc.findtext('author')
'David Beazley'
>>> doc.find('content')
<Element 'content' at 0x100776ec0>
>>> # A query involving a namespace (doesn't work)
>>> doc.find('content/html')
>>> # Works if fully qualified
>>> doc.find('content/{http://www.w3.org/1999/xhtml}html')
<Element '{http://www.w3.org/1999/xhtml}html' at 0x1007767e0>
>>> # Doesn't work
>>> doc.findtext('content/{http://www.w3.org/1999/xhtml}html/head/title')
>>> # Fully qualified
>>> doc.findtext('content/{http://www.w3.org/1999/xhtml}html/'
... '{http://www.w3.org/1999/xhtml}head/{http://www.w3.org/1999/xhtml}title')
'Hello World'
>>>

你可以通过将命名空间处理逻辑包装为一个工具类来简化这个过程:

class XMLNamespaces:
  def __init__(self, **kwargs):
    self.namespaces = {}
    for name, uri in kwargs.items():
      self.register(name, uri)
  def register(self, name, uri):
    self.namespaces[name] = '{'+uri+'}'
  def __call__(self, path):
    return path.format_map(self.namespaces)

通过下面的方式使用这个类:

>>> ns = XMLNamespaces(html='http://www.w3.org/1999/xhtml')
>>> doc.find(ns('content/{html}html'))
<Element '{http://www.w3.org/1999/xhtml}html' at 0x1007767e0>
>>> doc.findtext(ns('content/{html}html/{html}head/{html}title'))
'Hello World'
>>>

讨论
解析含有命名空间的XML文档会比较繁琐。 上面的 XMLNamespaces 仅仅是允许你使用缩略名代替完整的URI将其变得稍微简洁一点。

很不幸的是,在基本的 ElementTree 解析中没有任何途径获取命名空间的信息。 但是,如果你使用 iterparse() 函数的话就可以获取更多关于命名空间处理范围的信息。例如:

>>> from xml.etree.ElementTree import iterparse
>>> for evt, elem in iterparse('ns2.xml', ('end', 'start-ns', 'end-ns')):
... print(evt, elem)
...
end <Element 'author' at 0x10110de10>
start-ns ('', 'http://www.w3.org/1999/xhtml')
end <Element '{http://www.w3.org/1999/xhtml}title' at 0x1011131b0>
end <Element '{http://www.w3.org/1999/xhtml}head' at 0x1011130a8>
end <Element '{http://www.w3.org/1999/xhtml}h1' at 0x101113310>
end <Element '{http://www.w3.org/1999/xhtml}body' at 0x101113260>
end <Element '{http://www.w3.org/1999/xhtml}html' at 0x10110df70>
end-ns None
end <Element 'content' at 0x10110de68>
end <Element 'top' at 0x10110dd60>
>>> elem # This is the topmost element
<Element 'top' at 0x10110dd60>
>>>

最后一点,如果你要处理的XML文本除了要使用到其他高级XML特性外,还要使用到命名空间, 建议你最好是使用 lxml 函数库来代替 ElementTree 。 例如,lxml 对利用DTD验证文档、更好的XPath支持和一些其他高级XML特性等都提供了更好的支持。 这一小节其实只是教你如何让XML解析稍微简单一点。

Python 相关文章推荐
python smtplib模块发送SSL/TLS安全邮件实例
Apr 08 Python
浅谈python中字典append 到list 后值的改变问题
May 04 Python
Django如何自定义分页
Sep 25 Python
python 将大文件切分为多个小文件的实例
Jan 14 Python
Django框架中序列化和反序列化的例子
Aug 06 Python
python爬取Ajax动态加载网页过程解析
Sep 05 Python
利用Python发送邮件或发带附件的邮件
Nov 12 Python
Django model class Meta原理解析
Nov 14 Python
用python获取txt文件中关键字的数量
Dec 24 Python
详解python日志输出使用配置文件格式
Feb 10 Python
python 三边测量定位的实现代码
Apr 22 Python
Pandas实现批量拆分与合并Excel的示例代码
May 30 Python
详细解读Python中解析XML数据的方法
Oct 15 #Python
深入解析Python编程中JSON模块的使用
Oct 15 #Python
使用Python解析JSON数据的基本方法
Oct 15 #Python
深入讲解Python编程中的字符串
Oct 14 #Python
Python编程中字符串和列表的基本知识讲解
Oct 14 #Python
Python循环语句之break与continue的用法
Oct 14 #Python
Python编程中的for循环语句学习教程
Oct 14 #Python
You might like
php使用Jpgraph绘制饼状图的方法
2015/06/10 PHP
配置eAccelerator和XCache扩展来加速PHP程序的执行
2015/12/22 PHP
php版微信返回用户text输入的方法
2016/11/14 PHP
实例解析php的数据类型
2018/10/24 PHP
浅谈php的TS和NTS的区别
2019/03/13 PHP
JavaScript 组件之旅(四):测试 JavaScript 组件
2009/10/28 Javascript
基于JavaScript 类的使用详解
2013/05/07 Javascript
jQuery中after的两种用法实例
2013/07/03 Javascript
JS制作简单的三级联动
2015/03/18 Javascript
js实现简单排列组合的方法
2016/01/27 Javascript
js字符串操作总结(必看篇)
2016/11/22 Javascript
NPM 安装cordova时警告:npm WARN deprecated minimatch@2.0.10: Please update to minimatch 3.0.2 or higher to
2016/12/20 Javascript
利用vue重构有赞商城的思路以及总结整理
2019/02/21 Javascript
layui下拉列表select实现可输入查找的方法
2019/09/28 Javascript
vue el-upload上传文件的示例代码
2020/12/21 Vue.js
数据挖掘之Apriori算法详解和Python实现代码分享
2014/11/07 Python
学习python类方法与对象方法
2016/03/15 Python
python使用rpc框架gRPC的方法
2018/08/24 Python
pygame游戏之旅 添加游戏介绍
2018/11/20 Python
numpy数组广播的机制
2019/07/12 Python
python 列表推导式使用详解
2019/08/29 Python
Python hashlib模块实例使用详解
2019/12/24 Python
深入了解python列表(LIST)
2020/06/08 Python
浅谈TensorFlow之稀疏张量表示
2020/06/30 Python
解决PyCharm不在run输出运行结果而不是再Console里输出的问题
2020/09/21 Python
Python中正则表达式对单个字符,多个字符和匹配边界等使用
2021/01/27 Python
HTML5 Video标签的属性、方法和事件汇总介绍
2015/04/24 HTML / CSS
中东最大的在线宠物店:Dubai Pet Food
2020/06/11 全球购物
历史系毕业生自荐信
2013/10/28 职场文书
合作协议书怎么写
2014/04/18 职场文书
技术合作协议书范本
2014/04/18 职场文书
销售求职信范文
2014/05/26 职场文书
节能环保演讲稿
2014/08/28 职场文书
2014党委书记四风问题对照检查材料思想汇报
2014/09/22 职场文书
《我的伯父鲁迅先生》教学反思
2016/02/16 职场文书
sentinel支持的redis高可用集群配置详解
2022/04/01 Redis