如何告诉lxml.etree.tostring(element)不要在python中写命名空间?

问题描述 投票:0回答:4

我有一个巨大的 xml 文件(1 Gig)。我想将一些元素(条目)移动到具有相同标题和规范的另一个文件。

假设原始文件包含带有标签

<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.trostring

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()
的每一个参数似乎都没有帮助。有什么建议或者更正吗?

python namespaces lxml tostring elementtree
4个回答
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 声明。但不要超过第一个

>


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>

0
投票

这是一个古老的问题,但由于 13 年后 lxml 仍然没有内置这个明显的函数,处理它的最简单方法是使用正则表达式:

def get_text(element: etree.Element) -> str:
    s = etree.tostring(element).decode()
    if m := re.match(r'^<(\w*)[^>]*>((.|\n)+)<\/\1>', s):
        return m.group(2).strip()
    return s

这会剥离整个包含元素

<description blah blah blah><div>What we want</div><p>more stuff</p></description>
并仅返回
<description>
内的标记。为了安全起见,它会保存开始元素标记以在末尾进行匹配(使用
\1
反向引用),但寻找结束
<\
效果很好,因为贪婪匹配会跳过内容中嵌入的所有其他元素。

如果匹配失败,引发 ValueError 可能会更好,但这只会返回原始的丑陋字符串、命名空间和所有内容。

最新问题
© www.soinside.com 2019 - 2024. All rights reserved.