Jar*_*edL 9 python xml lxml elementtree xml-namespaces
Python的内置xml.etree包支持使用命名空间解析XML文件,但是命名空间前缀扩展到括在括号中的完整URI.所以在官方文档中的示例文件中:
<actors xmlns:fictional="http://characters.example.com"
xmlns="http://people.example.com">
<actor>
<name>John Cleese</name>
<fictional:character>Lancelot</fictional:character>
<fictional:character>Archie Leach</fictional:character>
</actor>
...
Run Code Online (Sandbox Code Playgroud)
该actor标签被扩大到{http://people.example.com}actor和fictional:character到{http://characters.example.com}character.
我可以看到这是如何使一切非常明确并减少歧义(文件可以具有相同的命名空间,具有不同的前缀等)但是使用起来非常麻烦.该Element.find()方法和其他方法允许将dict映射前缀传递给名称空间URI,所以我仍然可以做element.find('fictional:character', nsmap)但据我所知,标记属性没有任何类似之处.这导致了令人讨厌的东西element.attrib['{{{}}}attrname'.format(nsmap['prefix'])].
流行的lxml包提供了相同的API和一些扩展,其中一个扩展是nsmap它们从文档继承的元素的属性.然而,没有一种方法似乎实际上使用它,所以我仍然必须做element.find('fictional:character', element.nsmap),这只是不必要的重复输入每次.它仍然不适用于属性.
幸运的是lxml支持子类化BaseElement,所以我只使用p(for prefix)属性创建一个具有相同API的属性,但使用元素自动使用名称空间前缀nsmap(编辑:可能最好nsmap在代码中分配自定义).所以我只是这样做,element.p.find('fictional:character')或者element.p.attrib['prefix:attrname']更少重复,我认为更具可读性.
我只是觉得我真的错过了一些东西,它真的感觉这应该真的已经是一个功能,lxml如果不是内置etree包.我在某种程度上做错了吗?
你需要将它作为参数传递给每个函数调用吗?一个选项是设置要在属性中的XML文档中使用的前缀.
在将XML文档传递给第三方函数之前,这很好.该函数也希望使用前缀,因此它将属性设置为其他内容,因为它不知道您将其设置为什么.
一旦您获得XML文档,它就被修改,因此您的前缀不再起作用.
总而言之:不,它不安全,因此它很好.
这种设计不仅存在于Python中,它还存在于.NET中.在SelectNodes()[MSDN]可以,如果你不需要前缀使用.但只要有前缀,它就会抛出异常.因此,您必须使用使用XmlNamespaceManager作为参数的重载SelectNodes()[MSDN].
我建议学习XPath(lxml特定链接),你可以使用前缀.由于这可能是版本特定的,让我说我使用Python 2.7 x64和lxml 3.6.0运行此代码(我不太熟悉Python,所以这可能不是最干净的代码,但它很适合作为演示) :
from lxml import etree as ET
from pprint import pprint
data = """<?xml version="1.0"?>
<d:data xmlns:d="dns">
<country name="Liechtenstein">
<rank>1</rank>
<year>2008</year>
<gdppc>141100</gdppc>
<neighbor d:name="Austria" direction="E"/>
<neighbor name="Switzerland" direction="W"/>
</country>
<country name="Singapore">
<rank>4</rank>
<year>2011</year>
<gdppc>59900</gdppc>
<neighbor name="Malaysia" direction="N"/>
</country>
</d:data>"""
root = ET.fromstring(data)
my_namespaces = {'x':'dns'}
xp=root.xpath("/x:data/country/neighbor/@x:name", namespaces=my_namespaces)
pprint(xp)
xp=root.xpath("//@x:name", namespaces=my_namespaces)
pprint(xp)
xp=root.xpath("/x:data/country/neighbor/@name", namespaces=my_namespaces)
pprint(xp)
Run Code Online (Sandbox Code Playgroud)
输出是
C:\Python27x64\python.exe E:/xpath.py
['Austria']
['Austria']
['Switzerland', 'Malaysia']
Process finished with exit code 0
Run Code Online (Sandbox Code Playgroud)
注意XPath如何解决从x命名空间表中的d前缀到XML文档中的前缀的映射.
这消除了真正糟糕的阅读element.attrib['{{{}}}attrname'.format(nsmap['prefix'])].
要选择元素,请/element选择使用前缀.
xp=root.xpath("/x:data", namespaces=my_namespaces)
Run Code Online (Sandbox Code Playgroud)
要选择属性,请编写/@attribute,可选择使用前缀.
#See example above
Run Code Online (Sandbox Code Playgroud)
要向下导航,请连接几个元素.//如果您不知道介于两者之间的项目,请使用.要向上移动,请使用/...如果不遵循,属性必须是最后的/...
xp=root.xpath("/x:data/country/neighbor/@x:name/..", namespaces=my_namespaces)
Run Code Online (Sandbox Code Playgroud)
要使用条件,请将其写在方括号中./element[@attribute]表示:选择具有此属性的所有元素./element[@attribute='value']表示:选择具有此属性且属性具有特定值的所有元素./element[./subelement]表示:选择具有特定名称的子元素的所有元素.可选择在任何地方使用前缀
xp=root.xpath("/x:data/country[./neighbor[@name='Switzerland']]/@name", namespaces=my_namespaces)
Run Code Online (Sandbox Code Playgroud)
还有更多要发现的东西,比如text()兄弟选择的各种方式甚至功能.
原来的问题标题是
为什么使用XML命名空间在Python中看起来如此困难?
对于一些用户,他们只是不理解这个概念.如果用户理解这个概念,那么开发人员可能没有.也许这只是众多选择中的一个选择,而决定是朝这个方向发展.在这种情况下唯一可以对"为什么"部分给出答案的人就是开发者本人.
| 归档时间: |
|
| 查看次数: |
301 次 |
| 最近记录: |