Ответ 1
Проблема в том, что единственными действительными мнемическими объектами в XML являются quot
, amp
, apos
, lt
и gt
. Это означает, что почти все (X) HTML-названные сущности должны быть определены в DTD с помощью декларации объявления объекта, определенной в XML 1.1 spec. Если документ должен быть автономным, это должно быть сделано с помощью встроенного DTD, например:
<?xml version="1.1" ?>
<!DOCTYPE naughtyxml [
<!ENTITY nbsp " ">
<!ENTITY copy "©">
]>
<data>
<country name="Liechtenstein">
<rank>1 ></rank>
<year>2008©</year>
<gdppc>141100</gdppc>
<neighbor name="Austria" direction="E"/>
<neighbor name="Switzerland" direction="W"/>
</country>
</data>
XMLParser
xml.etree.ElementTree
использует xml.parsers.expat
для фактического разбора. В аргументах init для XMLParser
существует пространство для < предопределенных объектов HTML ', но этот аргумент еще не реализован. В методе init создается пустой dict с именем entity
, и это то, что используется для поиска объектов undefined.
Я не думаю, что expat (по расширению, ET XMLParser) способен обрабатывать переключение пространств имен на что-то вроде XHMTL, чтобы обойти это. Возможно потому, что он не будет определять внешние определения пространства имен (я попытался сделать xmlns="http://www.w3.org/1999/xhtml"
пространство имен по умолчанию для элемента данных, но оно не получилось хорошо), но я не могу это подтвердить. По умолчанию expat будет вызывать ошибку в отношении не XML-объектов, но вы можете обойти это, указав внешний DOCTYPE - это приведет к тому, что парсер expat передаст записи объектов undefined обратно в метод ET.XMLParser
_default()
.
Метод _default()
выполняет поиск entity
dict в экземпляре XMLParser
, и если он найдет соответствующий ключ, он заменит объект на соответствующее значение. Это поддерживает синтаксис Python-2.x, упомянутый в вопросе.
Решения:
- Если данные не имеют внешнего DOCTYPE и имеют (X) HTML-мнемонические объекты, вам не повезло. Недействительно XML и expat имеет право выбросить ошибку. Вы должны добавить внешний DOCTYPE.
- Если у данных есть внешний DOCTYPE, вы можете просто использовать свой старый синтаксис для сопоставления мнемонических имен с символами. Примечание: вы должны использовать
chr()
в py3k -unichr()
больше не является допустимым именем- В качестве альтернативы вы можете обновить
XMLParser.entity
с помощьюhtml.entities.html5
, чтобы сопоставить все допустимые мнемонические сущности HTML5 с их символами.
- В качестве альтернативы вы можете обновить
- Если данные XHTML, вы можете подклассифицировать
HTMLParser
для обработки мнемонических объектов, но это не вернетElementTree
по желанию.
Вот фрагмент, который я использовал, - он анализирует XML с внешним DOCTYPE через HTMLParser
(чтобы продемонстрировать, как добавить обработку объектов путем подкласса), ET.XMLParser
с сопоставлениями объектов и expat
(которые будут просто игнорировать undefined из-за внешнего DOCTYPE). Существует допустимый объект XML (>
) и объект undefined (©
), который я сопоставляю с chr(0x24B4)
с ET.XMLParser
.
from html.parser import HTMLParser
from html.entities import name2codepoint
import xml.etree.ElementTree as ET
import xml.parsers.expat as expat
xml = '''<?xml version="1.0"?>
<!DOCTYPE data PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<data>
<country name="Liechtenstein">
<rank>1></rank>
<year>2008©</year>
<gdppc>141100</gdppc>
<neighbor name="Austria" direction="E"/>
<neighbor name="Switzerland" direction="W"/>
</country>
</data>'''
# HTMLParser subclass which handles entities
print('=== HTMLParser')
class MyHTMLParser(HTMLParser):
def handle_starttag(self, name, attrs):
print('Start element:', name, attrs)
def handle_endtag(self, name):
print('End element:', name)
def handle_data(self, data):
print('Character data:', repr(data))
def handle_entityref(self, name):
self.handle_data(chr(name2codepoint[name]))
htmlparser = MyHTMLParser()
htmlparser.feed(xml)
# ET.XMLParser parse
print('=== XMLParser')
parser = ET.XMLParser()
parser.entity['copy'] = chr(0x24B8)
root = ET.fromstring(xml, parser)
print(ET.tostring(root))
for elem in root:
print(elem.tag, ' - ', elem.attrib)
for subelem in elem:
print(subelem.tag, ' - ', subelem.attrib, ' - ', subelem.text)
# Expat parse
def start_element(name, attrs):
print('Start element:', name, attrs)
def end_element(name):
print('End element:', name)
def char_data(data):
print('Character data:', repr(data))
print('=== Expat')
expatparser = expat.ParserCreate()
expatparser.StartElementHandler = start_element
expatparser.EndElementHandler = end_element
expatparser.CharacterDataHandler = char_data
expatparser.Parse(xml)