Python ElementTree模块:当使用“find”,“findall”方法时,如何忽略XML文件的命名空间以找到匹配的元素

时间:2022-03-03 23:18:45

I want to use the method of "findall" to locate some elements of the source xml file in the ElementTree module.

我想使用“findall”方法在ElementTree模块中找到源xml文件的一些元素。

However, the source xml file (test.xml) has namespace. I truncate part of xml file as sample:

但是,源xml文件(test.xml)具有命名空间。我将部分xml文件截断为样本:

<?xml version="1.0" encoding="iso-8859-1"?>
<XML_HEADER xmlns="http://www.test.com">
    <TYPE>Updates</TYPE>
    <DATE>9/26/2012 10:30:34 AM</DATE>
    <COPYRIGHT_NOTICE>All Rights Reserved.</COPYRIGHT_NOTICE>
    <LICENSE>newlicense.htm</LICENSE>
    <DEAL_LEVEL>
        <PAID_OFF>N</PAID_OFF>
        </DEAL_LEVEL>
</XML_HEADER>

The sample python code is below:

示例python代码如下:

from xml.etree import ElementTree as ET
tree = ET.parse(r"test.xml")
el1 = tree.findall("DEAL_LEVEL/PAID_OFF") # Return None
el2 = tree.findall("{http://www.test.com}DEAL_LEVEL/{http://www.test.com}PAID_OFF") # Return <Element '{http://www.test.com}DEAL_LEVEL/PAID_OFF' at 0xb78b90>

Although it can works, because there is a namespace "{http://www.test.com}", it's very inconvenient to add a namespace in front of each tag.

虽然它可以工作,因为有一个名称空间“{http://www.test.com}”,在每个标记前添加一个名称空间非常不方便。

How can I ignore the namespace when using the method of "find", "findall" and so on?

使用“find”,“findall”等方法时,如何忽略命名空间?

6 个解决方案

#1


39  

Instead of modifying the XML document itself, it's best to parse it and then modify the tags in the result. This way you can handle multiple namespaces and namespace aliases:

而不是修改XML文档本身,最好解析它,然后修改结果中的标记。这样您就可以处理多个名称空间和名称空间别名:

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

# instead of ET.fromstring(xml)
it = ET.iterparse(StringIO(xml))
for _, el in it:
    if '}' in el.tag:
        el.tag = el.tag.split('}', 1)[1]  # strip all namespaces
root = it.root

This is based on the discussion here: http://bugs.python.org/issue18304

这是基于这里的讨论:http://bugs.python.org/issue18304

#2


35  

If you remove the xmlns attribute from the xml before parsing it then there won't be a namespace prepended to each tag in the tree.

如果在解析之前从xml中删除xmlns属性,那么树中的每个标记都不会添加名称空间。

import re

xmlstring = re.sub(' xmlns="[^"]+"', '', xmlstring, count=1)

#3


14  

The answers so far explicitely put the namespace value in the script. For a more generic solution, I would rather extract the namespace from the xml:

到目前为止,答案明确地将命名空间值放在脚本中。对于更通用的解决方案,我宁愿从xml中提取命名空间:

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

And use it in find method:

并在find方法中使用它:

namespace = get_namespace(tree.getroot())
print tree.find('./{0}parent/{0}version'.format(namespace)).text

#4


10  

Here's an extension to nonagon's answer, which also strips namespaces off attributes:

这是对nonagon的答案的扩展,它也删除了属性的名称空间:

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

# instead of ET.fromstring(xml)
it = ET.iterparse(StringIO(xml))
for _, el in it:
    if '}' in el.tag:
        el.tag = el.tag.split('}', 1)[1]  # strip all namespaces
    for at in el.attrib.keys(): # strip namespaces of attributes too
        if '}' in at:
            newat = at.split('}', 1)[1]
            el.attrib[newat] = el.attrib[at]
            del el.attrib[at]
root = it.root

#5


3  

You can use the elegant string formatting construct as well:

您也可以使用优雅的字符串格式构造:

ns='http://www.test.com'
el2 = tree.findall("{%s}DEAL_LEVEL/{%s}PAID_OFF" %(ns,ns))

or, if you're sure that PAID_OFF only appears in one level in tree:

或者,如果您确定PAID_OFF仅出现在树中的一个级别:

el2 = tree.findall(".//{%s}PAID_OFF" % ns)

#6


0  

If you're using ElementTree and not cElementTree you can force Expat to ignore namespace processing by replacing ParserCreate():

如果您正在使用ElementTree而不是cElementTree,则可以通过替换ParserCreate()来强制Expat忽略名称空间处理:

from xml.parsers import expat
oldcreate = expat.ParserCreate
expat.ParserCreate = lambda encoding, sep: oldcreate(encoding, None)

ElementTree tries to use Expat by calling ParserCreate() but provides no option to not provide a namespace separator string, the above code will cause it to be ignore but be warned this could break other things.

ElementTree尝试通过调用ParserCreate()来使用Expat,但是没有提供不提供命名空间分隔符字符串的选项,上面的代码将导致它被忽略但是警告这可能会破坏其他东西。

#1


39  

Instead of modifying the XML document itself, it's best to parse it and then modify the tags in the result. This way you can handle multiple namespaces and namespace aliases:

而不是修改XML文档本身,最好解析它,然后修改结果中的标记。这样您就可以处理多个名称空间和名称空间别名:

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

# instead of ET.fromstring(xml)
it = ET.iterparse(StringIO(xml))
for _, el in it:
    if '}' in el.tag:
        el.tag = el.tag.split('}', 1)[1]  # strip all namespaces
root = it.root

This is based on the discussion here: http://bugs.python.org/issue18304

这是基于这里的讨论:http://bugs.python.org/issue18304

#2


35  

If you remove the xmlns attribute from the xml before parsing it then there won't be a namespace prepended to each tag in the tree.

如果在解析之前从xml中删除xmlns属性,那么树中的每个标记都不会添加名称空间。

import re

xmlstring = re.sub(' xmlns="[^"]+"', '', xmlstring, count=1)

#3


14  

The answers so far explicitely put the namespace value in the script. For a more generic solution, I would rather extract the namespace from the xml:

到目前为止,答案明确地将命名空间值放在脚本中。对于更通用的解决方案,我宁愿从xml中提取命名空间:

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

And use it in find method:

并在find方法中使用它:

namespace = get_namespace(tree.getroot())
print tree.find('./{0}parent/{0}version'.format(namespace)).text

#4


10  

Here's an extension to nonagon's answer, which also strips namespaces off attributes:

这是对nonagon的答案的扩展,它也删除了属性的名称空间:

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

# instead of ET.fromstring(xml)
it = ET.iterparse(StringIO(xml))
for _, el in it:
    if '}' in el.tag:
        el.tag = el.tag.split('}', 1)[1]  # strip all namespaces
    for at in el.attrib.keys(): # strip namespaces of attributes too
        if '}' in at:
            newat = at.split('}', 1)[1]
            el.attrib[newat] = el.attrib[at]
            del el.attrib[at]
root = it.root

#5


3  

You can use the elegant string formatting construct as well:

您也可以使用优雅的字符串格式构造:

ns='http://www.test.com'
el2 = tree.findall("{%s}DEAL_LEVEL/{%s}PAID_OFF" %(ns,ns))

or, if you're sure that PAID_OFF only appears in one level in tree:

或者,如果您确定PAID_OFF仅出现在树中的一个级别:

el2 = tree.findall(".//{%s}PAID_OFF" % ns)

#6


0  

If you're using ElementTree and not cElementTree you can force Expat to ignore namespace processing by replacing ParserCreate():

如果您正在使用ElementTree而不是cElementTree,则可以通过替换ParserCreate()来强制Expat忽略名称空间处理:

from xml.parsers import expat
oldcreate = expat.ParserCreate
expat.ParserCreate = lambda encoding, sep: oldcreate(encoding, None)

ElementTree tries to use Expat by calling ParserCreate() but provides no option to not provide a namespace separator string, the above code will cause it to be ignore but be warned this could break other things.

ElementTree尝试通过调用ParserCreate()来使用Expat,但是没有提供不提供命名空间分隔符字符串的选项,上面的代码将导致它被忽略但是警告这可能会破坏其他东西。