【问题标题】:Python: ElementTree, get the namespace string of an ElementPython:ElementTree,获取一个元素的命名空间字符串
【发布时间】:2012-03-19 18:57:42
【问题描述】:

此 XML 文件名为 example.xml:

<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

  <modelVersion>14.0.0</modelVersion>
  <groupId>.com.foobar.flubber</groupId>
  <artifactId>uberportalconf</artifactId>
  <version>13-SNAPSHOT</version>
  <packaging>pom</packaging>
  <name>Environment for UberPortalConf</name>
  <description>This is the description</description>    
  <properties>
      <birduberportal.version>11</birduberportal.version>
      <promotiondevice.version>9</promotiondevice.version>
      <foobarportal.version>6</foobarportal.version>
      <eventuberdevice.version>2</eventuberdevice.version>
  </properties>
  <!-- A lot more here, but as it is irrelevant for the problem I have removed it -->
</project>

如果我加载 example.xml 并使用 ElementTree 解析它,我可以看到它的命名空间是 http://maven.apache.org/POM/4.0.0

>>> from xml.etree import ElementTree
>>> tree = ElementTree.parse('example.xml')
>>> print tree.getroot()
<Element '{http://maven.apache.org/POM/4.0.0}project' at 0x26ee0f0>

我还没有找到一种方法来调用以从 Element 获取命名空间,而无需解析元素的 str(an_element)。看来一定有更好的办法。

【问题讨论】:

  • 你知道在这种情况下如何使用 find 方法吗?它在这里不起作用......

标签: python elementtree


【解决方案1】:

这是我在 ElementTree 3.9+ 上的解决方案,

def get_element_namespaces(filename, element):
    namespace = []
    for key, value in ET.iterparse(filename, events=['start', 'start-ns']):
        print(key, value)
        if key == 'start-ns':
            namespace.append(value)
        else:
            if ET.tostring(element) == ET.tostring(value):
                return namespace
            namespace = []
    return namespaces

这将返回一个 [prefix:URL] 元组数组,如下所示:

[('android', 'http://schemas.android.com/apk/res/android'), ('tools', 'http://schemas.android.com/tools')]

【讨论】:

    【解决方案2】:

    对于regular expression 来说,这是一项完美的任务。

    import re
    
    def namespace(element):
        m = re.match(r'\{.*\}', element.tag)
        return m.group(0) if m else ''
    

    【讨论】:

    • 在与这个问题斗争了一段时间后,这是我找到的最佳解决方案。我不敢相信 API 没有让您请求命名空间,同时,它在执行“rootElement.keys()”时不会返回属性“xmlns”。当然有一个很好的理由,但我现在找不到。
    • 在常规exp前添加r请完善这个答案。
    • @Jiu 非常感谢。我不敢相信我错过了。
    • 获取不包含大括号的命名空间:re.match(r'\{(.*)\}', element.tag).group(1)
    【解决方案3】:

    结合上面的一些答案,我认为最短的代码是

    theroot = tree.getroot()
    theroot.attrib[theroot.keys()[0]]
    

    【讨论】:

    • 这不准确,因为 xmlns 可能不是根的第一个属性。事实上,我目前正在尝试解析 TCX 文件,而 xmlns 根本没有显示为根的属性。
    【解决方案4】:

    简短的回答是:

    ElementTree._namspace_map[ElementTree._namspace_map.values().index('')]
    

    但前提是你一直在打电话

    ElementTree.register_namespace(prefix,uri)
    

    响应在遍历

    的结果时收到的每个 event=="start-ns"
    ET.iterparse(...) 
    

    你注册了“start-ns”

    回答“什么是默认命名空间?”这个问题,有必要明确两点:

    (1) XML 规范说默认命名空间不一定在整个树中都是全局的,而是可以在根下的任何元素处重新声明默认命名空间,并向下继承,直到遇到另一个默认命名空间重新声明。

    (2) ElementTree 模块可以(事实上)处理没有根默认命名空间的类 XML 文档,如果它们在文档中的任何地方都没有命名空间使用。 (* 可能有不那么严格的条件,例如,“如果”而不一定是“iff”)。

    可能还值得考虑“你想要它做什么?”考虑到 XML 文件在语义上是等价的,但在语法上却大不相同。例如,以下三个文件在语义上是等价的,但是 A.xml 有一个默认命名空间声明,B.xml 有三个,C.xml 没有。

    A.xml:
    <a xlmns="http://A" xlmns:nsB0="http://B0" xlmns:nsB1="http://B1">
         <nsB0:b/>
         <nsB1:b/>
    </a>
    
    B.xml:
    <a xlmns="http://A">
         <b xlmns="http://B0"/>
         <b xlmns="http://B1"/>
    </a>
    
    C.xml:
    <{http://A}a>
         <{http://B0}b/>
         <{http://B1}b/>
    </a>
    

    文件 C.xml 是呈现给 ElementTree 搜索函数的规范扩展句法表示。

    如果您事先确定不会发生命名空间冲突,则可以在解析时修改元素标签,如下所述:Python ElementTree module: How to ignore the namespace of XML files to locate matching element when using the method "find", "findall"

    【讨论】:

      【解决方案5】:

      lxml.xtree 库的元素有一个名为 nsmap 的字典,其中显示了当前标记范围内正在使用的所有命名空间。

      >>> item = tree.getroot().iter().next()
      >>> item.nsmap
      {'md': 'urn:oasis:names:tc:SAML:2.0:metadata'}
      

      【讨论】:

        【解决方案6】:

        不使用正则表达式:

        >>> root
        <Element '{http://www.google.com/schemas/sitemap/0.84}urlset' at 0x2f7cc10>
        
        >>> root.tag.split('}')[0].strip('{')
        'http://www.google.com/schemas/sitemap/0.84'
        

        【讨论】:

        • 类似的答案root.tag[1:root.tag.index('}')]
        • 这个答案是最简单的。谢谢!
        【解决方案7】:

        命名空间应该在Element.tag“实际”标签之前:

        >>> root = tree.getroot()
        >>> root.tag
        '{http://maven.apache.org/POM/4.0.0}project'
        

        要了解有关命名空间的更多信息,请查看ElementTree: Working with Namespaces and Qualified Names

        【讨论】:

        • 您提供的链接已失效,您可能需要对其进行编辑以指向该信息的替代来源。
        【解决方案8】:

        我认为看一下属性会更容易:

        >>> root.attrib
        {'{http://www.w3.org/2001/XMLSchema-instance}schemaLocation':
           'http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd'}
        

        【讨论】:

        • 当然比解析str(the_element) 容易。但我想解析the_element.tag 更容易一些。因为我只对命名空间感兴趣。你怎么看?
        • 我认为@RikPoggi 的答案似乎是最好的答案(实际上,我赞成)。事实上,获取命名空间应该像re.search('\{(.*)\}', the_element.tag).group(1) 一样简单。根据我的回答,看起来您可以使用the_element.attrib.values()[0].split()[0],但实际上,它看起来并不那么简单,并且不能保证您将来不会获得任何其他属性。
        【解决方案9】:

        我不确定xml.etree 是否可以做到这一点,但您可以通过lxml.etree 做到这一点:

        >>> from lxml import etree
        >>> tree = etree.parse('example.xml')
        >>> tree.xpath('namespace-uri(.)')
        'http://maven.apache.org/POM/4.0.0'
        

        【讨论】:

        • 我在 Windows 中使用 Python 2.7.2 得到 unresolved import: etreexpath 在使用 xml.etree 时不能作为一种方法使用,如果我使用 find()(支持 xpath 表达式),'namespace-uri(.)' 语句仍然不起作用。
        • 这正是我想要的,see pr on gh
        • 这是我见过的最好的解决方案。我通常使用 xmlstarlet,但我现在可以切换。
        • 对于lxml,获取命名空间的更简单方法是tree.getroot().nsmap
        • @Jona:我假设使用None 是一种处理默认命名空间的方法,即声明没有前缀的命名空间。
        猜你喜欢
        • 2015-01-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-02-18
        • 1970-01-01
        • 1970-01-01
        • 2016-03-05
        • 2021-10-19
        相关资源
        最近更新 更多