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数据结构之图的实现方法
Jul 08 Python
Python实现TCP协议下的端口映射功能的脚本程序示例
Jun 14 Python
python3第三方爬虫库BeautifulSoup4安装教程
Jun 19 Python
详解Django中CBV(Class Base Views)模型源码分析
Feb 25 Python
提升Python程序性能的7个习惯
Apr 14 Python
python交易记录链的实现过程详解
Jul 03 Python
Python编程学习之如何判断3个数的大小
Aug 07 Python
python 修改本地网络配置的方法
Aug 14 Python
使用tensorflow显示pb模型的所有网络结点方式
Jan 23 Python
Python和Bash结合在一起的方法
Nov 13 Python
Python xlwings插入Excel图片的实现方法
Feb 26 Python
Flask搭建一个API服务器的步骤
May 28 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 向访客和爬虫显示不同的内容
2009/11/09 PHP
PHP中static关键字原理的学习研究分析
2011/07/18 PHP
对比分析php中Cookie与Session的异同
2016/02/19 PHP
ThinkPHP实现转换数据库查询结果数据到对应类型的方法
2017/11/16 PHP
jQuery插件开发全解析
2012/10/10 Javascript
jQuery点击自身以外地方关闭弹出层的简单实例
2013/12/24 Javascript
Jqgrid设置全选(选择)及获取选择行的值示例代码
2013/12/28 Javascript
JavaScript实现动态创建CSS样式规则方案
2014/09/06 Javascript
JavaScript实现的字符串replaceAll函数代码分享
2015/04/02 Javascript
Javascript下拉刷新的简单实现
2017/02/14 Javascript
使用vue框架 Ajax获取数据列表并用BootStrap显示出来
2017/04/24 Javascript
详解从angular-cli:1.0.0-beta.28.3升级到@angular/cli:1.0.0
2017/05/22 Javascript
移动端效果之Swiper详解
2017/10/09 Javascript
微信小程序tabBar模板用法实例分析【附demo源码下载】
2017/11/28 Javascript
Vue按需加载的具体实现
2017/12/02 Javascript
vue内置指令详解
2018/04/03 Javascript
在vue2.0中引用element-ui组件库的方法
2018/06/21 Javascript
跟老齐学Python之dict()的操作方法
2014/09/24 Python
python3 判断列表是一个空列表的方法
2018/05/04 Python
Python3中的json模块使用详解
2018/05/05 Python
Python使用matplotlib绘制三维参数曲线操作示例
2019/09/10 Python
Python字典常见操作实例小结【定义、添加、删除、遍历】
2019/10/25 Python
Django自带的加密算法及加密模块详解
2019/12/03 Python
Python使用enumerate获取迭代元素下标
2020/02/03 Python
Python 解析xml文件的示例
2020/09/29 Python
澳大利亚百货公司:David Jones
2018/02/08 全球购物
普通院校学生的自荐信
2013/11/27 职场文书
革命先烈的英雄事迹材料
2014/02/15 职场文书
高中军训感言500字
2014/02/24 职场文书
生日庆典策划方案
2014/06/02 职场文书
软件工程毕业生自荐信
2014/07/04 职场文书
人力资源职位说明书
2014/07/29 职场文书
领导班子四风问题对照检查材料
2014/09/27 职场文书
idea编译器vue缩进报错问题场景分析
2021/07/04 Vue.js
SQL中的三种去重方法小结
2021/11/01 SQL Server
Spring Security使用单点登录的权限功能
2022/04/03 Java/Android