- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
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>
...
actor
标签扩展为 {http://people.example.com}actor
和 fictional:character
为 {http://characters.example.com}字符
。
我可以看到这如何使一切都非常明确并减少歧义(文件可以具有相同的 namespace 但具有不同的前缀等),但使用起来非常麻烦。 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
(前缀)属性创建了一个,它具有相同的 API 但自动使用使用元素的 nsmap
的 namespace 前缀(编辑: 可能最好分配代码中定义的自定义 nsmap
)。所以我只做 element.p.find('fictional:character')
或 element.p.attrib['prefix:attrname']
,这样重复性要低得多,而且我想办法更具可读性。
我只是觉得我真的遗漏了一些东西,如果不是内置的 etree
包的话,这真的应该已经是 lxml
的一个特性了。我是不是做错了?
最佳答案
是否需要将其作为参数传递到每个函数调用中?一个选项是在属性中设置要在 XML 文档中使用的前缀。
这很好,直到您将 XML 文档传递给第 3 方函数。该函数也想使用前缀,因此它将属性设置为其他内容,因为它不知道您将其设置为什么。
一旦您取回 XML 文档,它就被修改了,因此您的前缀不再起作用。
总而言之:不,它不安全,因此它很好。
这种设计不仅存在于 Python 中,它也存在于 .NET 中。 SelectNodes()
[MSDN]如果不需要前缀,可以使用。但是只要存在前缀,它就会抛出异常。因此,您必须使用 the overloaded SelectNodes()
[MSDN]它使用 XmlNamespaceManager 作为参数。
我建议学习XPath (lxml specific link) ,您可以在其中使用前缀。由于这可能是特定于版本的,假设我使用 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)
输出是
C:\Python27x64\python.exe E:/xpath.py
['Austria']
['Austria']
['Switzerland', 'Malaysia']
Process finished with exit code 0
注意 XPath 如何很好地解决了从命名空间表中的 x
前缀到 XML 文档中的 d
前缀的映射。
这消除了真正可怕的阅读 element.attrib['{{{}}}attrname'.format(nsmap['prefix'])]
。
要选择一个元素,请编写 /element
,可选择使用前缀。
xp=root.xpath("/x:data", namespaces=my_namespaces)
要选择属性,请编写 /@attribute
,可选择使用前缀。
#See example above
要向下导航,请连接多个元素。如果您不知道中间的项目,请使用 //
。要向上移动,请使用 /..
。如果后面没有 /..
,则属性必须放在最后。
xp=root.xpath("/x:data/country/neighbor/@x:name/..", namespaces=my_namespaces)
要使用条件,请将其写在方括号中。 /element[@attribute]
表示:选择所有具有该属性的元素。 /element[@attribute='value']
表示:选择所有具有该属性且该属性具有特定值的元素。 /element[./subelement]
表示:选择具有特定名称的子元素的所有元素。可选择在任何地方使用前缀。
xp=root.xpath("/x:data/country[./neighbor[@name='Switzerland']]/@name", namespaces=my_namespaces)
还有很多东西有待发现,例如 text()
、各种兄弟选择方式甚至函数。
原来的问题标题是
Why does working with XML namespaces seem so difficult in Python?
对于一些用户来说,他们只是不理解这个概念。如果用户理解这个概念,开发者可能不理解。也许这只是众多选择中的一个,而决定就是朝这个方向发展。在这种情况下,唯一能够回答“为什么”部分的人就是开发人员本人。
关于python - 在 Python 中使用 XML namespace 时,如何使我的代码更具可读性和 DRYer?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37263363/
大家好:我正在尝试创建一个命名空间,以便我可以在整个应用程序中的不同 CoffeeScript 文件中使用一个类(至少这是我对命名空间用途的理解) 我在这里找到了一个很好的例子:Classes wit
我想使用两个字符串(我不知道它们的内容)来创建两个 namespace 。如果 namespace 存在,我不想创建一个新的 namespace 。这是我的代码: function createNam
在 Struts 2 中,我看到根命名空间提供与根命名空间相同的行为,即充当“包罗万象”。我需要限制我的应用程序中的操作只能从一个 URL 访问,包括 URL 中没有 namespace 的操作。我的
我想在旧的代码库中包含新的 SASS。考虑到已经编写的新样式,我如何避免新样式泄漏。 例如 .box width: 100% // ... .tab display: inline-blo
我的\命名空间 \My\Namespace 那么,我应该使用哪一个,我看到了 php documentation主要使用 My\Namespace。 但据说 \My\Namespace 更好,因为没有
我正在研究 Rails 2.3.8。我的 environment.rb 中有以下内容 config.gem "redis" config.gem "redis-namespace", :lib =>
我有一个包含一些类型定义的小命名空间,我用它来使我的代码看起来更简洁。但是,我不想在每个使用这些类型之一的文件中添加“using namespace ...”行,毕竟我已经必须为文件添加 #inclu
如何获取 MediaWiki namespace 列表?最好有姓名和号码。 最佳答案 使用API:api.php?action=query&meta=siteinfo&siprop=namespa
为了使用“namespace import *”命令在不同的命名空间中使用该命名空间的变量/过程,“命名空间导出...”是否是必需的。我们真的应该在源命名空间中进行“导出”并在目标命名空间中进行“导入
假设我有以下 list ,例如部署,服务和入口。在默认 namespace 中,一切正常。虽然我想将资源投入到用manifest.yaml创建的另一个命名空间。我以为如果我写了 kubectl app
我想以编程方式将一个类从一个命名空间移动到另一个命名空间。这包括调整移动类在其先前命名空间中的任何依赖项。 我猜我可以以某种方式利用 Roslyn 项目,但我找不到起点。 编辑: 我正在尝试在 C#
Spring不同类型的注入方式 spring官网代码示例 1、不同类型的注入方式 <?xml version="1.0" encoding="UTF-8&qu
我想知道,考虑到这段代码: namespace A\B; use A\B as AB; use \Z\V as ZV; // another used namespace class Merry {
我正在研究一个似乎一切都很好的“董事会”类(class)。不知何故,在其他类(class)上工作了大约一个小时后,Board 在错误方面表现出一些非常奇怪的行为。 //headerfile #prag
我尝试在 TYPO3 扩展中创建多个 ViewHelper。 但是现在我尝试将 ViewHelper 放在子目录中,例如 扩展\类\ View 助手\自定义。 ViewHelper 的新
我的情况是:日历属于客户或销售员 因为我还有像 Event 和 File 这样的类,所以我将命名空间 App\Models 用于我所有的模型类。 所以我设置了多态关系: 在日历.php public
所有 Less 文档和教程都使用 #namespace > .mixin()当它进入命名空间时的语法。但是我发现自己更习惯于 .namespace.mixin()语法,即: .namespace()
我正在尝试使用 MS Robotics Studio 和 VS 2008 构建 DSS 服务,但是在构建时,我收到来自 dssproxy.exe 的错误消息: The class MyServ
例如,我们有两个用于解析简历的类,一个用于解析Excel,另一个用于解析HTML。我的同事们喜欢做的就是将这两个类命名为相同的名称,并将它们放在不同的命名空间中,如下所示: namespace XX.
我的库的所有类都在一个命名空间中定义。当我为 Doxygen 创建主页时,我必须在注释中明确使用这个命名空间来让 Doxygen 生成链接。我想对整个注释块使用“使用命名空间”之类的东西。 一个例子:
我是一名优秀的程序员,十分优秀!