如何让lxml.etree.tostring(element)在Python中不写入命名空间?
我有一个很大的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声明为止。但要注意,不要超过第一个>
。