在我的 S1000D xml 中,它指定了一个引用公共(public) URL 的 DOCTYPE,该 URL 包含对包含所有有效字符实体的许多其他文件的引用。我已经使用 xml.etree.ElementTree 和 lxml 来尝试解析它并得到一个解析错误,两者都指示:
undefined entity −: line 82, column 652
尽管−
根据指定的 ENTITY Reference 是一个有效的实体。
xml顶层如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dmodule [
<!ENTITY % ISOEntities PUBLIC 'ISO 8879-1986//ENTITIES ISO Character Entities 20030531//EN//XML' 'http://www.s1000d.org/S1000D_4-1/ent/ISOEntities'>
%ISOEntities;]>
如果你出去得到http://www.s1000d.org/S1000D_4-1/ent/ISOEntities ,它将包括 20 个其他 ent 文件,其中一个名为 iso-tech.ent,其中包含以下行:
<!ENTITY minus "−"> <!-- MINUS SIGN -->
在靠近 652 列的 xml 文件的第 82 行中,如下所示:
....引用70 −
41....
如何在不获取未定义实体的情况下运行 python 脚本来解析此文件?
抱歉,我不想指定 parser.entity['minus'] = chr(2212)
例如。我这样做是为了快速修复,但有很多字符实体引用。
我希望解析器检查 xml 中指定的实体引用。
我很惊讶,但我绕着太阳转了一圈又回来了,还没有找到如何做到这一点(或者也许我已经找到但无法理解)。
如果我更新我的 xml 文件并添加
<!ENTITY minus "−">
它不会失败,所以它不是 xml。
解析失败。这是我用于 ElementTree 的代码
fl = os.path.join(pth, fn)
try:
root = ET.parse(fl)
except ParseError as p:
print("ParseError : ", p)
这是我用于 lxml 的代码
fl = os.path.join(pth, fn)
try:
parser = etree.XMLParser(load_dtd=True, resolve_entities=True)
root = etree.parse(fl, parser=parser)
except etree.XMLSyntaxError as pe:
print("lxml XMLSyntaxError: ", pe)
我希望解析器加载 ENTITY 引用,以便它知道 - 以及所有文件中指定的所有其他字符实体都是有效的实体字符。
非常感谢您的建议和帮助。
最佳答案
我要回答 lxml。如果可以使用 lxml,就没有理由考虑 ElementTree。
我认为您缺少的部分是 XMLParser 中的 no_network=False
;这是True by default .
例子...
XML 输入 (test.xml)
<!DOCTYPE doc [
<!ENTITY % ISOEntities PUBLIC 'ISO 8879-1986//ENTITIES ISO Character Entities 20030531//EN//XML' 'http://www.s1000d.org/S1000D_4-1/ent/ISOEntities'>
%ISOEntities;]>
<doc>
<test>Here's a test of minus: −</test>
</doc>
python
from lxml import etree
parser = etree.XMLParser(load_dtd=True,
no_network=False)
tree = etree.parse("test.xml", parser=parser)
etree.dump(tree.getroot())
输出
<doc>
<test>Here's a test of minus: −</test>
</doc>
如果您希望保留实体引用,请将 resolve_entities=False
添加到 XMLParser。
此外,与其去外部位置解析参数实体,不如考虑设置一个 XML Catalog .这将使您能够将公共(public)和/或系统标识符解析为本地版本。
使用上面相同的 XML 输入的示例...
XML 目录(目录“catalog test”中的“catalog.xml”(目录名称中用于测试的空间))
<!DOCTYPE catalog PUBLIC "-//OASIS//DTD XML Catalogs V1.1//EN" "http://www.oasis-open.org/committees/entity/release/1.1/catalog.dtd">
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
<!-- The path in @uri is relative to this file (catalog.xml). -->
<uri name="http://www.s1000d.org/S1000D_4-1/ent/ISOEntities" uri="./ents/ISOEntities_stackoverflow.ent"/>
</catalog>
实体文件(目录“catalog test/ents”中的“ISOEntities_stackoverflow.ent”。将值更改为“BAM!”以进行测试)
<!ENTITY minus "BAM!">
Python(将 no_network
更改为 True
以进一步证明本地版本的 http://www.s1000d.org/S1000D_4-1/ent/ISOEntities
正在使用。)
import os
from urllib.request import pathname2url
from lxml import etree
# The XML_CATALOG_FILES environment variable is used by libxml2 (which is used by lxml).
# See http://xmlsoft.org/catalog.html.
try:
xcf_env = os.environ['XML_CATALOG_FILES']
except KeyError:
# Path to catalog must be a url.
catalog_path = f"file:{pathname2url(os.path.join(os.getcwd(), 'catalog test/catalog.xml'))}"
# Temporarily set the environment variable.
os.environ['XML_CATALOG_FILES'] = catalog_path
parser = etree.XMLParser(load_dtd=True,
no_network=True)
tree = etree.parse("test.xml", parser=parser)
etree.dump(tree.getroot())
输出
<doc>
<test>Here's a test of minus: BAM!</test>
</doc>
关于python - 使用 Python 解析 XML 解析外部实体引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55307786/