【问题标题】:Suppressing namespace prefixes in ElementTree 1.2在 ElementTree 1.2 中抑制命名空间前缀
【发布时间】:2011-12-28 03:16:57
【问题描述】:

在 python 2.7(使用 etree 1.3)中,我可以抑制元素上的 XML 前缀,如下所示:

Python 2.7.1 (r271:86832, Jun 16 2011, 16:59:05) 
[GCC 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import xml.etree.ElementTree as etree
>>> etree.VERSION
'1.3.0'
>>> something = etree.Element('{http://some.namespace}token')
>>> etree.tostring(something)
'<ns0:token xmlns:ns0="http://some.namespace" />'
>>> etree.register_namespace('', 'http://some.namespace')
>>> etree.tostring(something)
'<token xmlns="http://some.namespace" />'

register_namespace 函数是在 1.3 中添加的。我正在尝试以与版本 1.2.6 的 python 2.6 的 etree 兼容的方式删除前缀。这是我尝试过的:

Python 2.6.7 (r267:88850, Jul 31 2011, 19:30:54) 
[GCC 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import xml.etree.ElementTree as etree
>>> etree.VERSION
'1.2.6'
>>> something = etree.Element('{http://some.namespace}token')
>>> etree.tostring(something)
'<ns0:token xmlns:ns0="http://some.namespace" />'
>>> etree._namespace_map['http://some.namespace'] = ''
>>> etree.tostring(something)
'<:token xmlns:="http://some.namespace" />'

这不是我想要的。前缀仍然存在,但为空白。有什么办法可以彻底去除?

【问题讨论】:

标签: python xml xml-serialization elementtree


【解决方案1】:

查看source code for ElementTree in python2.6 后,: 被硬编码在fixtag 函数中。作为一种解决方法,这就是我所做的:

from xml.etree import ElementTree as etree

if etree.VERSION[0:3] == '1.2':
    #in etree < 1.3, this is a workaround for supressing prefixes

    def fixtag(tag, namespaces):
        import string
        # given a decorated tag (of the form {uri}tag), return prefixed
        # tag and namespace declaration, if any
        if isinstance(tag, etree.QName):
            tag = tag.text
        namespace_uri, tag = string.split(tag[1:], "}", 1)
        prefix = namespaces.get(namespace_uri)
        if namespace_uri not in namespaces:
            prefix = etree._namespace_map.get(namespace_uri)
            if namespace_uri not in etree._namespace_map:
                prefix = "ns%d" % len(namespaces)
            namespaces[namespace_uri] = prefix
            if prefix == "xml":
                xmlns = None
            else:
                if prefix is not None:
                    nsprefix = ':' + prefix
                else:
                    nsprefix = ''
                xmlns = ("xmlns%s" % nsprefix, namespace_uri)
        else:
            xmlns = None
        if prefix is not None:
            prefix += ":"
        else:
            prefix = ''

        return "%s%s" % (prefix, tag), xmlns

    etree.fixtag = fixtag
    etree._namespace_map['http://some.namespace'] = None
else:
    #For etree > 1.3, use register_namespace function
    etree.register_namespace('', 'http://some.namespace')

如果这篇文章过时了,代码将保留here

【讨论】:

【解决方案2】:

我在我的文件顶部创建了一个方法,并在标签字符串应该存在的地方简单地使用它。我将其命名为“ns_tag”,但您可以随意命名。

def ns_tag(tag):
    return str( ElementTree.QName('http://some.namespace/api/4/', tag) )

例子:

root = ElementTree.fromstring(xml)
success = root.find(ns_tag('success'))
if success.text == 'true':
    for node in root.find(ns_tag('items')):
        id = node.find(ns_tag('id')).text
        ...

【讨论】:

  • 这有助于处理文件,但如果您保存文件,前缀仍会存在。这个问题是关于在保存文件时抑制前缀。
【解决方案3】:

这是一个 hack,但这对我来说在 Jython 2.5.2 上效果很好

    ns=re.match('^\{([^\}]+)\}', mydoc.getroot().tag ).group(1)
    etree._namespace_map[ns]='STRIPME'
    etree.tostring( mydoc.getroot() ).replace( 'STRIPME:', '' )

【讨论】:

    【解决方案4】:

    为了从标签中删除命名空间,我使用了这段代码,其中的标签对象是 TreeBuilder 的子类。在 start 和 end 两个方法中,我们都删除了不需要的命名空间。

    from xml.etree.ElementTree import XML, XMLParser, tostring, TreeBuilder
    
    class StripNamespace(TreeBuilder):
        def start(self, tag, attrib):
            index = tag.find('}')
            if index != -1:
                tag = tag[index+1:]
            super(StripNamespace, self).start(tag, attrib)
        def end(self, tag):
            index = tag.find('}')
            if index != -1:
                tag = tag[index+1:]
            super(StripNamespace, self).end(tag)
    
    target = StripNamespace()
    parser = XMLParser(target=target)
    with open("DmsDesc.xml") as f:
        content = f.read()
    root = XML(content, parser=parser)
    print tostring(root)
    

    简单漂亮。

    【讨论】: