Python lxml.etree 保留实体引用
Python lxml.etree retain entity references
我正在创建一个简单的脚本来使用特定模式解析、验证、修复和重新打印 XML 文件。整个过程很好,但问题是当我打印修改后的 ElementTree 时,它会删除我所有的实体引用。
这是简化的 python 代码:
from pathlib import Path
from lxml import etree as ET
from lxml.builder import E
schema = ET.XMLSchema('C:/path/to/schema.xsd')
parser = ET.XMLParser(recover=True)
source_file = Path('file.xml')
tree = ET.parse(source_file.name, parser, base_url="http://www.domain.url")
root = tree.getroot()
# Do some validation
source_file.write_text(ET.tostring(tree, encoding='utf-8').decode(encoding='utf-8'), encoding='utf-8')
这是 'before' XML 的片段:
<!DOCTYPE element [
<!ENTITY % ISOEntities PUBLIC "ISO 8879-1986//ENTITIES ISO Character Entities 20030531//EN//XML" "http://www.domain.url/path/to/ISOEntities">
%ISOEntities
]>
<para>−67 to 250°</para>
之后:
<!DOCTYPE element [
<!ENTITY % ISOEntities PUBLIC "ISO 8879-1986//ENTITIES ISO Character Entities 20030531//EN//XML" "http://www.domain.url/path/to/ISOEntities">
<! -- THE ENTIRE CONTENTS OF ISOENTITIES (100s of lines of code) -->
]>
<para>-67 to 250°</para>
虽然技术上 'correct',但我希望将它们保留为实体引用而不是文字字符。如前所述,它还解析了我不想要的 ISOEntities
。
现在,我尝试的明显解决方案是将 resolve_entities=False
kwarg 添加到解析器。结果是完全删除了引用并简单地用任何东西替换它们。
<!DOCTYPE element [
<!ENTITY % ISOEntities PUBLIC "ISO 8879-1986//ENTITIES ISO Character Entities 20030531//EN//XML" "http://www.domain.url/path/to/ISOEntities">
%ISOEntities
]>
<para>67 to 250</para>
有什么方法可以像解析时一样将树打印成字符串吗? (即保持内部 DTD 相同并保持实体引用完整)
编辑:使用调试器验证实体在 tostring
操作之前丢失,因此肯定是解析过程消除了它们,而不是转换为字符串。
所以我没有找到这个问题的好的答案。实体都在 %IsoEntities 中声明,但因为那是 本身也是一个实体 ,我将它设置为不解析实体,解析器不解析 %IsoEntities,因此不会'也不认识任何其他实体。
但我确实找到了解决方法。原来 &
没有被替换,我猜是因为这是一个独特的案例。因此解决方法是将所有 &
替换为 &
。所以你会发送类似 &minus;
的内容。解析器不会将其识别为一个实体,并将保持原样。将 ElementTree 转换为字符串格式后,您可以再次遍历并将所有 &
替换为 &
,这样您就可以再次得到原始实体。
我仍然很想听听是否有人有更好的答案。
我正在创建一个简单的脚本来使用特定模式解析、验证、修复和重新打印 XML 文件。整个过程很好,但问题是当我打印修改后的 ElementTree 时,它会删除我所有的实体引用。
这是简化的 python 代码:
from pathlib import Path
from lxml import etree as ET
from lxml.builder import E
schema = ET.XMLSchema('C:/path/to/schema.xsd')
parser = ET.XMLParser(recover=True)
source_file = Path('file.xml')
tree = ET.parse(source_file.name, parser, base_url="http://www.domain.url")
root = tree.getroot()
# Do some validation
source_file.write_text(ET.tostring(tree, encoding='utf-8').decode(encoding='utf-8'), encoding='utf-8')
这是 'before' XML 的片段:
<!DOCTYPE element [
<!ENTITY % ISOEntities PUBLIC "ISO 8879-1986//ENTITIES ISO Character Entities 20030531//EN//XML" "http://www.domain.url/path/to/ISOEntities">
%ISOEntities
]>
<para>−67 to 250°</para>
之后:
<!DOCTYPE element [
<!ENTITY % ISOEntities PUBLIC "ISO 8879-1986//ENTITIES ISO Character Entities 20030531//EN//XML" "http://www.domain.url/path/to/ISOEntities">
<! -- THE ENTIRE CONTENTS OF ISOENTITIES (100s of lines of code) -->
]>
<para>-67 to 250°</para>
虽然技术上 'correct',但我希望将它们保留为实体引用而不是文字字符。如前所述,它还解析了我不想要的 ISOEntities
。
现在,我尝试的明显解决方案是将 resolve_entities=False
kwarg 添加到解析器。结果是完全删除了引用并简单地用任何东西替换它们。
<!DOCTYPE element [
<!ENTITY % ISOEntities PUBLIC "ISO 8879-1986//ENTITIES ISO Character Entities 20030531//EN//XML" "http://www.domain.url/path/to/ISOEntities">
%ISOEntities
]>
<para>67 to 250</para>
有什么方法可以像解析时一样将树打印成字符串吗? (即保持内部 DTD 相同并保持实体引用完整)
编辑:使用调试器验证实体在 tostring
操作之前丢失,因此肯定是解析过程消除了它们,而不是转换为字符串。
所以我没有找到这个问题的好的答案。实体都在 %IsoEntities 中声明,但因为那是 本身也是一个实体 ,我将它设置为不解析实体,解析器不解析 %IsoEntities,因此不会'也不认识任何其他实体。
但我确实找到了解决方法。原来 &
没有被替换,我猜是因为这是一个独特的案例。因此解决方法是将所有 &
替换为 &
。所以你会发送类似 &minus;
的内容。解析器不会将其识别为一个实体,并将保持原样。将 ElementTree 转换为字符串格式后,您可以再次遍历并将所有 &
替换为 &
,这样您就可以再次得到原始实体。
我仍然很想听听是否有人有更好的答案。