Python lmxl增量XML序列化重复名称空间
我目前正在用lxml在Python中序列化一些较大的XML文件。我想使用增量编写器来实现这一点。我的XML格式严重依赖于名称空间和属性。当我运行以下代码时Python lmxl增量XML序列化重复名称空间,python,xml,xml-serialization,lxml,xml-namespaces,Python,Xml,Xml Serialization,Lxml,Xml Namespaces,我目前正在用lxml在Python中序列化一些较大的XML文件。我想使用增量编写器来实现这一点。我的XML格式严重依赖于名称空间和属性。当我运行以下代码时 from io import BytesIO from lxml import etree sink = BytesIO() nsmap = { 'test': 'http://test.org', 'foo': 'http://foo.org', 'bar': 'http://bar.org', } with
from io import BytesIO
from lxml import etree
sink = BytesIO()
nsmap = {
'test': 'http://test.org',
'foo': 'http://foo.org',
'bar': 'http://bar.org',
}
with etree.xmlfile(sink) as xf:
with xf.element("test:testElement", nsmap=nsmap):
name = etree.QName(nsmap["foo"], "fooElement")
elem = etree.Element(name)
xf.write(elem)
print(sink.getvalue().decode('utf-8'))
然后我得到以下输出:
<test:testElement xmlns:bar="http://bar.org"
xmlns:foo="http://foo.org"
xmlns:test="http://test.org">
<ns0:fooElement xmlns:ns0="http://foo.org"/>
</test:testElement>
如何使lxml只在根目录中添加名称空间,子目录使用正确的前缀?我想我需要使用etree.Element
,因为我需要向节点添加一些属性
什么不起作用:
1) 使用register\u名称空间
for prefix, uri in nsmap.items():
etree.register_namespace(prefix, uri)
这仍然会重复,但会使前缀正确。我不太喜欢它,因为它改变了全球的东西
2) 在元素中指定nsmap
:
elem = etree.Element(name, nsmap=nsmap)
屈服
<foo:fooElement xmlns:bar="http://bar.org"
xmlns:foo="http://foo.org"
xmlns:test="http://test.org"/>
打印
None
您需要创建一个子元素:
_nsmap={
'test': 'http://test.org',
'foo': 'http://foo.org',
'bar': 'http://bar.org',
}
root = etree.Element(
"{http://bar.org}test",
creator='SO',
nsmap=_nsmap
)
doc = etree.ElementTree(root)
name = etree.QName(_nsmap["foo"], "fooElement")
elem = etree.SubElement(root, name)
doc.write('/tmp/foo.xml', xml_declaration=True, encoding='utf-8', pretty_print=True)
print (open('/tmp/foo.xml').read())
返回:
<?xml version='1.0' encoding='UTF-8'?>
<bar:test xmlns:bar="http://bar.org" xmlns:foo="http://foo.org" xmlns:test="http://test.org" creator="SO">
<foo:fooElement/>
</bar:test>
有可能生产出与您想要的产品相近的产品:
from io import BytesIO
from lxml import etree
sink = BytesIO()
nsmap = {
'test': 'http://test.org',
'foo': 'http://foo.org',
'bar': 'http://bar.org',
}
with etree.xmlfile(sink) as xf:
with xf.element("test:testElement", nsmap=nsmap):
with xf.element("foo:fooElement"):
pass
print(sink.getvalue().decode('utf-8'))
这将生成XML:
<test:testElement xmlns:bar="http://bar.org" xmlns:foo="http://foo.org" xmlns:test="http://test.org"><foo:fooElement></foo:fooElement></test:testElement>
额外的名称空间声明消失了,但是您得到的不是立即结束元素,而是foo:fooElement
的一对开始和结束标记
我查看了
lxml.etree.xmlfile
的源代码,没有看到那里的代码维护着这样一种状态,即它将检查以了解已经声明了哪些名称空间,并避免不必要地再次声明它们。有可能我错过了什么,但我真的不认为我错过了。增量XML序列化程序的要点是在不使用内存的情况下运行。当内存不是问题时,您只需创建一个表示XML文档的对象树,并将其序列化即可。您需要付出巨大的内存成本,因为在序列化树之前,整个树都必须在内存中可用。通过使用增量序列化程序,可以避免内存问题。为了最大限度地节省内存,序列化程序必须最小化它维护的状态量。如果它在序列化中生成一个元素时要考虑该元素的父元素,那么它必须“记住”父元素是什么,并保持状态。在最坏的情况下,它将保持如此多的状态,以至于与仅仅创建一个XML对象树并进行序列化相比没有任何好处。我如何将子元素
与etree.xmlfile
一起使用?我没有根,使用xf
会给出TypeError:参数“\u parent”的类型不正确(应为lxml.etree.\u元素,获得lxml.etree.\u IncrementalFileWriter)
这个问题有很多好处,但OP似乎忘记了它。也许这个答案并不能解决问题,但值得投票表决。
from io import BytesIO
from lxml import etree
sink = BytesIO()
nsmap = {
'test': 'http://test.org',
'foo': 'http://foo.org',
'bar': 'http://bar.org',
}
with etree.xmlfile(sink) as xf:
with xf.element("test:testElement", nsmap=nsmap):
with xf.element("foo:fooElement"):
pass
print(sink.getvalue().decode('utf-8'))
<test:testElement xmlns:bar="http://bar.org" xmlns:foo="http://foo.org" xmlns:test="http://test.org"><foo:fooElement></foo:fooElement></test:testElement>