当在源文件的根元素中定义名称空间时,lxml
将在输出中复制所有名称空间。我需要使用xml.etree
执行此操作。更好的办法是仅输出已使用的那些,但xml.etree
找不到全部。
一种解决方案是使用root.set()
强制添加名称空间。但是,这将复制xml.etree
确实找到的任何名称空间,如下所示。
适合在命令提示符下粘贴的完整示例:
import xml.etree.ElementTree as ET
try:
from io import StringIO
except ImportError:
from StringIO import StringIO
def get_namespaces(sourcestring):
sourcefile = StringIO(sourcestring)
return dict(
[node for _, node in ET.iterparse(sourcefile, events=['start-ns'])])
ET._namespace_map = dict() # remove any previously registered namespaces
sourcetext = (
'<desc xmlns="uri_a" xmlns:b="uri_b" xmlns:c="uri_c"'
' b:foo="c:bar">a</desc>')
source = ET.fromstring(sourcetext)
namespaces = get_namespaces(sourcetext)
for prefix, uri in namespaces.items():
ET.register_namespace(prefix, uri)
if prefix:
tag = 'xmlns:' + prefix
else:
tag = 'xmlns'
source.set(tag, uri)
print(ET.tostring(source, encoding='unicode'))
结果导致我的应用程序失败:
<desc xmlns="uri_a" xmlns:b="uri_b" xmlns="uri_a" xmlns:b="uri_b" xmlns:c="uri_c" b:foo="c:bar">a</desc>
这类似于Forcing xml.etree to output "unused" namespaces,但是名称空间来自源文件,因此Python代码无法识别它们。
首先,在不添加缺少名称空间的情况下生成输出。获取从该输出中找到的名称空间。然后,通过添加未找到的名称空间来生成最终输出。
def add_namespaces_not_found(root):
result_with_namespaces_found = ET.tostring(root, encoding='unicode')
namespaces_found = get_namespaces(result_with_namespaces_found)
for prefix, uri in namespaces.items():
if prefix not in namespaces_found:
if prefix:
tag = 'xmlns:' + prefix
else:
tag = 'xmlns'
root.set(tag, uri)
结果:
<desc xmlns="uri_a" xmlns:b="uri_b" xmlns:c="uri_c" b:foo="c:bar">a</desc>
不需要不需要两次生成输出的解决方案。