如何让lxml.etree.tostring(element)在Python中不写入命名空间?

15 投票
3 回答
12040 浏览
提问于 2025-04-16 23:14

我有一个很大的xml文件(1GB)。我想把一些元素(条目)移动到另一个文件里,保持相同的头部和格式。

假设原始文件里有一个标签为 <to_move> 的条目:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE some SYSTEM "some.dtd">
<some>
...
<to_move date="somedate">
    <child>some text</child>
    ...
...
</to_move>
...
</some>

我使用 lxml.etree.iterparse 来遍历这个文件。效果很好。当我找到标签为 <to_move> 的元素时,假设它存储在变量 element 中,我会这样做:

new_file.write(etree.tostring(element))

但是这样做的结果是:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE some SYSTEM "some.dtd">
<some>
...
<to_move xmlns:="some" date="somedate">  # <---- Here is the problem. I don't want the namespace.
    <child>some text</child>
    ...
...
</to_move>
...
</some>

所以问题是:如何让 etree.tostring() 不写入 xmlns:="some" 这个部分?这可能吗?我在 lxml.etree 的 API 文档里挣扎了很久,但找不到令人满意的答案。

这是我找到的关于 etree.tostring 的内容:

tostring(element_or_tree, encoding=None, method="xml",
xml_declaration=None, pretty_print=False, with_tail=True,
standalone=None, doctype=None, exclusive=False, with_comments=True)

将一个元素序列化为其 XML 树的编码字符串表示。

对我来说,tostring() 的每一个参数似乎都没有帮助。有什么建议或更正吗?

3 个回答

2

这段话是对'unutbu'的回答的评论,提到希望能有一个清理命名空间的建议,但没有给出具体的例子。也许这就是你想要的内容……

from lxml import objectify
objectify.deannotate(root, cleanup_namespaces=True)
2

有一种方法可以通过使用XSLT去除命名空间

import io
import lxml.etree as ET


def remove_namespaces(doc):
    # http://wiki.tei-c.org/index.php/Remove-Namespaces.xsl
    xslt='''<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="no"/>

    <xsl:template match="/|comment()|processing-instruction()">
        <xsl:copy>
          <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="*">
        <xsl:element name="{local-name()}">
          <xsl:apply-templates select="@*|node()"/>
        </xsl:element>
    </xsl:template>

    <xsl:template match="@*">
        <xsl:attribute name="{local-name()}">
          <xsl:value-of select="."/>
        </xsl:attribute>
    </xsl:template>
    </xsl:stylesheet>
    '''

    xslt_doc = ET.parse(io.BytesIO(xslt))
    transform = ET.XSLT(xslt_doc)
    doc = transform(doc)
    return doc

doc = ET.parse('data.xml')
doc = remove_namespaces(doc)
print(ET.tostring(doc))

结果是

<some>

<to_move date="somedate">
    <child>some text</child>
</to_move>

</some>
4

我经常会用一个命名空间来给它起个别名,像这样:

someXML = lxml.etree.XML(someString)
if ns is None:
      ns = {"m": someXML.tag.split("}")[0][1:]}
someid = someXML.xpath('.//m:ImportantThing//m:ID', namespaces=ns)

你也可以用类似的方法来获取命名空间,然后写一个正则表达式,用来在使用tostring之后清理它。

或者你可以先清理输入的字符串。找到第一个空格,检查它后面是不是跟着xmlns,如果是,就把从这个xmlns开始到下一个空格之间的内容都删掉;如果不是,就只删掉这个空格。这样重复操作,直到没有更多的空格或者xmlns声明为止。但要注意,不要超过第一个>

撰写回答