我目前正在使用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 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>
如您所见,foo
的名称空间是重复的,而不是我的前缀:
<ns0:fooElement xmlns:ns0="http://foo.org"/>
如何使lxml仅在根目录中添加名称空间,而子级从那里使用正确的前缀?我认为我需要使用etree.Element
,因为我需要向节点添加一些属性。
什么不起作用:
1)使用register_namespace
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"/>
fooElement
。
我也查看了lxml的文档和源代码,但是Cython真的很难阅读和搜索。 xf.element
的上下文管理器不返回该元素。例如
with xf.element('foo:fooElement') as e:
print(e)
打印None
。
答案 0 :(得分:1)
有可能产生与您想要的东西接近的东西:
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对象树然后进行序列化将没有任何好处。
答案 1 :(得分:0)
您需要创建一个子元素:
_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>