Разрешить внешнюю неразобранную сущность при разборе с помощью lxml
Как я могу разрешить Внешнюю Непарсированную Сущность во время синтаксического анализа с lxml?
Вот мой пример кода:
import io
from lxml import etree
content = b"""\
<?xml version="1.0"?>
<!DOCTYPE sample [
<!NOTATION jpeg SYSTEM "image/jpeg">
<!ENTITY ref1 SYSTEM "python-logo-small.jpg" NDATA jpeg>
<!ELEMENT sample EMPTY>
<!ATTLIST sample src ENTITY #REQUIRED>
]>
<sample src="ref1"/>
"""
parser = etree.XMLParser(dtd_validation=True, resolve_entities=True)
doc = etree.parse(io.BytesIO(content), parser=parser)
print(etree.tostring(doc))
Примечание: я использую lxml >= 3.4
В настоящее время у меня есть следующий результат:
<!DOCTYPE sample [
<!NOTATION jpeg SYSTEM "image/jpeg" >
<!ENTITY ref1 SYSTEM "python-logo-small.jpg" NDATA jpeg>
<!ELEMENT sample EMPTY>
<!ATTLIST sample src ENTITY #REQUIRED>
]>
<sample src="ref1"/>
Здесь ref1
сущность не разрешена в "python-logo-small.jpg". Я ожидал <sample src="python-logo-small.jpg"/>
, Что-то не так?
Я также пытаюсь с:
parser = etree.XMLParser(dtd_validation=True, resolve_entities=True, load_dtd=True)
Но у меня такой же результат.
В качестве альтернативы, я бы хотел самостоятельно восстановить сущности. Для этого я попытаюсь перечислить сущности таким образом:
for entity in doc.docinfo.internalDTD.iterentities():
msg_fmt = "{entity.name!r}, {entity.content!r}, {entity.orig!r}"
print(msg_fmt.format(entity=entity))
Но я получаю только имена сущностей и обозначений, а не определение сущности:
'ref1', 'jpeg', None
Как получить доступ к определению сущности?
2 ответа
ОК, невозможно "разрешить" внешние неразобранные объекты, но мы можем перечислить их:
import io
import xml.sax
content = b"""\
<?xml version="1.0"?>
<!DOCTYPE sample [
<!NOTATION jpeg SYSTEM "image/jpeg">
<!ENTITY ref1 SYSTEM "python-logo-small.jpg" NDATA jpeg>
<!ELEMENT sample EMPTY>
<!ATTLIST sample src ENTITY #REQUIRED>
]>
<sample src="ref1"/>
"""
class MyDTDHandler(xml.sax.handler.DTDHandler):
def __init__(self):
pass
def unparsedEntityDecl(self, name, publicId, systemId, ndata):
print(dict(name=name, publicId=publicId, systemId=systemId, ndata=ndata))
xml.sax.handler.DTDHandler.unparsedEntityDecl(self, name, publicId, systemId, ndata)
parser = xml.sax.make_parser()
parser.setDTDHandler(MyDTDHandler())
parser.parse(io.BytesIO(content))
Результат:
{'systemId': u'python-logo-small.jpg', 'ndata': u'jpeg', 'publicId': None, 'name': u'ref1'}
Итак, работа выполнена.
Документ XML с неразобранной сущностью выглядит хорошо. Но неразобранные сущности не решаются так, как вы, кажется, ожидаете. Если вы хотите увидеть <sample src="python-logo-small.jpg"/>
в проанализированном выводе используйте внутреннюю (проанализированную) сущность.
Пример:
import io
from lxml import etree
content = b"""\
<?xml version="1.0"?>
<!DOCTYPE sample [
<!ENTITY ref1 "python-logo-small.jpg">
<!ELEMENT sample EMPTY>
<!ATTLIST sample src CDATA #REQUIRED>
]>
<sample src="&ref1;"/>
"""
parser = etree.XMLParser(dtd_validation=True, resolve_entities=True)
doc = etree.parse(io.BytesIO(content), parser=parser)
print(etree.tostring(doc))
Выход:
<!DOCTYPE sample [
<!ENTITY ref1 "python-logo-small.jpg">
<!ELEMENT sample EMPTY>
<!ATTLIST sample src CDATA #REQUIRED>
]>
<sample src="python-logo-small.jpg"/>
Заметки:
-
ref1
Сущность объявлена как внутренняя сущность. - На объект ссылаются с
&ref1;
, -
src
атрибут объявлен как типCDATA
,
Вы можете получить значение (URI) неразобранных сущностей с помощью XSLT, используя unparsed-entity-uri
функция. Чтобы увидеть это в действии, добавьте следующие строки в пример кода в вопросе:
xsl = etree.XML('''\
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="utf-8" omit-xml-declaration="yes"/>
<xsl:template match="sample">
<xsl:value-of select="unparsed-entity-uri(@src)"/>
</xsl:template>
</xsl:stylesheet>
''')
transform = etree.XSLT(xsl)
result = transform(doc)
print result
Выход:
python-logo-small.jpg