【问题标题】:Why doesn't Element.attrib include namespace definitions?为什么 Element.attrib 不包含名称空间定义?
【发布时间】:2022-12-03 00:43:34
【问题描述】:

我想创建一个 XML 命名空间映射(例如,在 findall 调用中使用,就像在 the Python documentation of ElementTree 中一样)。鉴于定义似乎作为 xbrl 根元素的属性存在,我想我可以检查我的 root 元素中的 attrib 属性ElementTree。然而,下面的代码

from io import StringIO
import xml.etree.ElementTree as ET


TEST = '''<?xml version="1.0" encoding="utf-8"?>
<xbrl
  xml:lang="en-US"
  xmlns="http://www.xbrl.org/2003/instance"
  xmlns:country="http://xbrl.sec.gov/country/2021"
  xmlns:dei="http://xbrl.sec.gov/dei/2021q4"
  xmlns:iso4217="http://www.xbrl.org/2003/iso4217"
  xmlns:link="http://www.xbrl.org/2003/linkbase"
  xmlns:nvda="http://www.nvidia.com/20220130"
  xmlns:srt="http://fasb.org/srt/2021-01-31"
  xmlns:stpr="http://xbrl.sec.gov/stpr/2021"
  xmlns:us-gaap="http://fasb.org/us-gaap/2021-01-31"
  xmlns:xbrldi="http://xbrl.org/2006/xbrldi"
  xmlns:xlink="http://www.w3.org/1999/xlink"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  </xbrl>'''


xbrl = ET.parse(StringIO(TEST))
print(xbrl.getroot().attrib)

产生以下输出:

{'{http://www.w3.org/XML/1998/namespace}lang': 'en-US'}

为什么root.attrib 中没有显示任何名称空间属性?我至少希望 xlmns 出现在字典中,因为它没有前缀。

我尝试了什么?

以下代码似乎可以生成名称空间映射:

print({prefix: uri for key, (prefix, uri) in ET.iterparse(StringIO(TEST), events=['start-ns'])})

输出:

{'': 'http://www.xbrl.org/2003/instance',
'country': 'http://xbrl.sec.gov/country/2021',
'dei': 'http://xbrl.sec.gov/dei/2021q4',
'iso4217': 'http://www.xbrl.org/2003/iso4217',
'link': 'http://www.xbrl.org/2003/linkbase',
'nvda': 'http://www.nvidia.com/20220130',
'srt': 'http://fasb.org/srt/2021-01-31',
'stpr': 'http://xbrl.sec.gov/stpr/2021',
'us-gaap': 'http://fasb.org/us-gaap/2021-01-31',
'xbrldi': 'http://xbrl.org/2006/xbrldi',
'xlink': 'http://www.w3.org/1999/xlink',
'xsi': 'http://www.w3.org/2001/XMLSchema-instance'}

但是,哎呀,不得不两次解析文件是不是很恶心。

【问题讨论】:

  • 诸如 xmlns="http://www.w3.org/2000/svg" 之类的命名空间声明在语法上是一个属性。但是正如您所注意到的,它不是解析 XML 文档时创建的数据结构中的属性。 XML 信息集规范将属性和名称空间定义为单独的“信息项”:en.wikipedia.org/wiki/XML_Information_Set。另见rpbourret.com/xml/NamespacesFAQ.htm#decl
  • 但是你不只需要提取一次名称空间吗?如果您事先不知道稍后将在 xpath 中使用“nvda:whatever”,为什么还要费心将 xmlns:nvda="http://www.nvidia.com/20220130" 添加到名称空间映射?你可以做一次 iterparse 的事情作为一种简单的方法来为这种类型的文档获取一个命名空间字典,然后根据需要将它复制到你的脚本中。更一般地说,名称空间通常暗示您必须了解的模式才能知道要搜索哪种类型的东西。名称空间字典似乎只是其中的一小部分。
  • lxml 将在元素上包含一个 nsmap,因此您可以更改为使用不同的工具集。命名空间可以在任何元素上声明,因此您仍然需要扫描。
  • @tdelaney 我知道我正在搜索 nvda:whatever,但 uris 以不同的频率变化(nvda,大约每季度一次)。例如,在下一季度的财务结果中,xmlns:nvda="http://www.nvidia.com/20220430"。感觉就像我必须更改每个财务报告的硬编码命名空间映射,即使我知道我一直在寻找 nvda:units
  • 命名空间应该很少更改……仅当该命名空间的模式发生更改时。可能添加或删除了字段。我有点惊讶这些看起来比较新。我离开 XML 业务已有一段时间了,但对于我正在从事的工作,模式 URI 更改是一个值得注意的事件。我很想跟踪当前模式,如果脚本未能正确扫描 xml,则查找更改。不过,在所有情况下可能都不是令人满意的解决方案。

标签: python xml elementtree xml-namespaces


【解决方案1】:

至于您的具体问题的答案,为什么 attrib 列表不包含命名空间前缀 decls,对于不明确的答案感到抱歉:因为它们不是属性。

http://www.w3.org/XML/1998/namespace 是一种特殊模式,它的行为与用户空间中的其他模式不同。在表示,xmlns:prefix="uri"是一个属性。在所有其他从属(通过解析序列)模式,xmlns:prefix="uri"是一个特殊的东西,一个命名空间前缀声明,它不同于节点或元素上的属性。我没有这方面的参考,但它在我使用过的至少六个(正确的)XML 解析器实现中完美地适用,包括来自 IBM、Microsoft 和 Oracle 的解析器。

至于重新解析文件的丑陋,我感到你的痛苦,但这是必要的。正如 tdelaney 所指出的那样,您可能不会假设您的所有名称空间声明或前缀必须在你的根元素上。

做好准备的可能性使用不同的名称空间重新定义相同的前缀每个节点在你的文档中。这可能是正确的,并且库必须正确地使用它,即使您的文档从来不是这种情况(或者更糟,如果它是迄今为止从未如此).

考虑一下当可能有更好的解决方案(如 XPath 或 XQuery)时,您是否正在硬塞一些文本处理来解析或查询 XML。 Saxon 的 Python 包装器和 Python 包装器最近发生了一些不错的变化,尽管它们的定价模型已经改变。

【讨论】:

    猜你喜欢
    • 2011-09-30
    • 1970-01-01
    • 2011-09-30
    • 1970-01-01
    • 2018-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-09
    相关资源
    最近更新 更多