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素数检测的方法
May 11 Python
python使用urllib2提交http post请求的方法
May 26 Python
Python实现处理管道的方法
Jun 04 Python
在Django的上下文中设置变量的方法
Jul 20 Python
python编写弹球游戏的实现代码
Mar 12 Python
python异步编程 使用yield from过程解析
Sep 25 Python
python GUI库图形界面开发之PyQt5窗口类QMainWindow详细使用方法
Feb 26 Python
配置python的编程环境之Anaconda + VSCode的教程
Mar 29 Python
python网络编程:socketserver的基本使用方法实例分析
Apr 09 Python
关于Keras Dense层整理
May 21 Python
Python3交互式shell ipython3安装及使用详解
Jul 11 Python
Python接口自动化测试框架运行原理及流程
Nov 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 Sql Server连接失败问题及解决办法
2009/08/07 PHP
php提示无法加载或mcrypt没有找到 PHP 扩展 mbstring解决办法
2012/03/27 PHP
php封装的page分页类完整实例
2016/10/18 PHP
PHP微信网页授权的配置文件操作分析
2019/05/29 PHP
基于PHP实现发微博动态代码实例
2020/12/11 PHP
JQuery 绑定事件时传递参数的实现方法
2009/10/13 Javascript
JavaScript 获取当前时间戳的代码
2010/08/05 Javascript
详解axios在node.js中的post使用
2017/04/27 Javascript
Vue 进阶教程之v-model详解
2017/05/06 Javascript
浅析为什么a=&quot;abc&quot; 不等于 a=new String(&quot;abc&quot;)
2017/10/25 Javascript
jQuery实现的简单图片轮播效果完整示例
2018/02/08 jQuery
Vue.js的动态组件模板的实现
2018/11/26 Javascript
Element Notification通知的实现示例
2020/07/27 Javascript
node.js 如何监视文件变化
2020/09/01 Javascript
JavaScript实现简单动态表格
2020/12/02 Javascript
PYTHON正则表达式 re模块使用说明
2011/05/19 Python
搞清楚 Python traceback的具体使用方法
2019/05/13 Python
pyQT5 实现窗体之间传值的示例
2019/06/20 Python
django 微信网页授权登陆的实现
2019/07/30 Python
利用python下载scihub成文献为PDF操作
2020/07/09 Python
python爬虫实现爬取同一个网站的多页数据的实例讲解
2021/01/18 Python
CSS3中的@keyframes关键帧动画的选择器绑定
2016/06/13 HTML / CSS
html5 canvas手势解锁源码分享
2020/01/07 HTML / CSS
Myprotein加拿大官网:欧洲第一的运动营养品牌
2018/01/06 全球购物
《一个中国孩子的呼声》教学反思
2014/02/12 职场文书
财务人员的自我评价范文
2014/03/03 职场文书
报关报检委托书
2014/04/08 职场文书
房屋出售协议书
2014/04/10 职场文书
幼儿教师辞职信
2015/02/27 职场文书
假如给我三天光明读书笔记
2015/06/26 职场文书
总经理聘用协议书
2015/09/21 职场文书
2019通用版导游词范本!
2019/08/07 职场文书
《文化苦旅》读后感:阅读,让人诗意地栖居在大地上
2019/12/24 职场文书
vue中data改变后让视图同步更新的方法
2021/03/29 Vue.js
Mysql数据库索引面试题(程序员基础技能)
2021/05/31 MySQL
Django中celery的使用项目实例
2022/07/07 Python