xml.dom.minidom — Минимальная реализация DOM

Источник: Lib/xml/dom/minidom.py


xml.dom.minidom - это минимальная реализация интерфейса Document Object Model, с API, аналогичным тем, что есть в других языках. Он задуман как более простой, чем полный DOM, а также значительно меньше по размеру. Пользователям, которые еще не освоили DOM, следует рассмотреть возможность использования модуля xml.etree.ElementTree для обработки XML.

Предупреждение

Модуль xml.dom.minidom не защищен от злонамеренно созданных данных. Если вам нужно разобрать недоверенные или неаутентифицированные данные, смотрите Уязвимости XML.

Приложения DOM обычно начинаются с разбора некоторого XML в DOM. В xml.dom.minidom это делается с помощью функций разбора:

from xml.dom.minidom import parse, parseString

dom1 = parse('c:\\temp\\mydata.xml')  # parse an XML file by name

datasource = open('c:\\temp\\mydata.xml')
dom2 = parse(datasource)  # parse an open file

dom3 = parseString('<myxml>Some data<empty/> some more data</myxml>')

Функция parse() может принимать либо имя файла, либо объект открытого файла.

xml.dom.minidom.parse(filename_or_file, parser=None, bufsize=None)

Возвращает Document из заданного ввода. filename_или_file может быть либо именем файла, либо файлоподобным объектом. parser, если задан, должен быть объектом парсера SAX2. Эта функция изменит обработчик документов парсера и активирует поддержку пространств имен; другие настройки парсера (например, установка распознавателя сущностей) должны быть сделаны заранее.

Если в строке содержится XML, вместо него можно использовать функцию parseString():

xml.dom.minidom.parseString(string, parser=None)

Возвращает объект Document, представляющий строку. Этот метод создает объект io.StringIO для строки и передает его в parse().

Обе функции возвращают объект Document, представляющий содержимое документа.

Функции parse() и parseString() соединяют парсер XML с «конструктором DOM», который может принимать события разбора от любого парсера SAX и преобразовывать их в дерево DOM. Названия функций, возможно, вводят в заблуждение, но их легко понять при изучении интерфейсов. Разбор документа будет завершен до того, как эти функции вернутся; просто эти функции сами не предоставляют реализацию парсера.

Вы также можете создать Document, вызвав метод на объекте «DOM Implementation». Этот объект можно получить, вызвав функцию getDOMImplementation() в пакете xml.dom или в модуле xml.dom.minidom. Когда у вас есть Document, вы можете добавить к нему дочерние узлы, чтобы заполнить DOM:

from xml.dom.minidom import getDOMImplementation

impl = getDOMImplementation()

newdoc = impl.createDocument(None, "some_tag", None)
top_element = newdoc.documentElement
text = newdoc.createTextNode('Some textual content.')
top_element.appendChild(text)

Когда у вас есть объект документа DOM, вы можете получить доступ к частям вашего XML-документа через его свойства и методы. Эти свойства определены в спецификации DOM. Основным свойством объекта документа является свойство documentElement. Оно указывает на главный элемент XML-документа: тот, который удерживает все остальные. Вот пример программы:

dom3 = parseString("<myxml>Some data</myxml>")
assert dom3.documentElement.tagName == "myxml"

Когда вы заканчиваете работу с DOM-деревом, вы можете вызвать метод unlink(), чтобы способствовать скорейшей очистке ненужных объектов. unlink() - это xml.dom.minidom-специфическое расширение API DOM, которое делает узел и его потомков по сути бесполезными. В противном случае сборщик мусора Python в конце концов позаботится об объектах в дереве.

См.также

Document Object Model (DOM) Level 1 Specification

Рекомендация W3C для DOM, поддерживаемая xml.dom.minidom.

Объекты DOM

Определение DOM API для Python дано в документации к модулю xml.dom. В этом разделе перечислены различия между API и xml.dom.minidom.

Разбивает внутренние ссылки внутри DOM так, что он будет собираться в мусор на версиях Python без циклического GC. Даже если циклический GC доступен, использование этой функции может сделать большие объемы памяти доступными раньше, поэтому вызов этой функции для объектов DOM, как только они больше не нужны, является хорошей практикой. Эта функция должна вызываться только на объекте Document, но может вызываться на дочерних узлах, чтобы отбросить детей этого узла.

Вы можете избежать явного вызова этого метода, используя оператор with. Следующий код автоматически отсоединит dom при выходе из блока with:

with xml.dom.minidom.parse(datasource) as dom:
    ... # Work with dom.
Node.writexml(writer, indent='', addindent='', newl='', encoding=None, standalone=None)

Запись XML в объект writer. Объект writer принимает на вход тексты, но не байты, у него должен быть метод write(), соответствующий методу интерфейса объекта file. Параметр indent - это отступ текущего узла. Параметр addindent - это дополнительный отступ, который следует использовать для подузлов текущего узла. Параметр newl задает строку для завершения новых строк.

Для узла Document можно использовать дополнительный ключевой аргумент encoding, чтобы указать поле кодировки в заголовке XML.

Аналогично, явное указание аргумента standalone приводит к тому, что объявления отдельного документа добавляются в пролог XML-документа. Если значение установлено в True, добавляется standalone="yes", в противном случае устанавливается значение "no". Если аргумент не указан, декларация будет опущена в документе.

Изменено в версии 3.8: Метод writexml() теперь сохраняет порядок атрибутов, указанный пользователем.

Изменено в версии 3.9: Добавлен параметр standalone.

Node.toxml(encoding=None, standalone=None)

Возвращает строку или байтовую строку, содержащую XML, представленный узлом DOM.

При явном аргументе encoding [1] результатом будет байтовая строка в указанной кодировке. При отсутствии аргумента encoding результатом будет строка Юникода, а в объявлении XML в результирующей строке не указана кодировка. Кодирование этой строки в кодировке, отличной от UTF-8, скорее всего, будет неправильным, поскольку UTF-8 является кодировкой XML по умолчанию.

Аргумент standalone ведет себя точно так же, как в writexml().

Изменено в версии 3.8: Метод toxml() теперь сохраняет порядок атрибутов, указанный пользователем.

Изменено в версии 3.9: Добавлен параметр standalone.

Node.toprettyxml(indent='\t', newl='\n', encoding=None, standalone=None)

Возвращает красиво напечатанную версию документа. indent задает строку отступа, по умолчанию - табулятор; newl задает строку, выдаваемую в конце каждой строки, по умолчанию - \n.

Аргумент encoding ведет себя так же, как и соответствующий аргумент toxml().

Аргумент standalone ведет себя точно так же, как в writexml().

Изменено в версии 3.8: Метод toprettyxml() теперь сохраняет порядок атрибутов, указанный пользователем.

Изменено в версии 3.9: Добавлен параметр standalone.

Пример DOM

Этот пример программы представляет собой достаточно реалистичный пример простой программы. В данном конкретном случае мы не используем все преимущества гибкости DOM.

import xml.dom.minidom

document = """\
<slideshow>
<title>Demo slideshow</title>
<slide><title>Slide title</title>
<point>This is a demo</point>
<point>Of a program for processing slides</point>
</slide>

<slide><title>Another demo slide</title>
<point>It is important</point>
<point>To have more than</point>
<point>one slide</point>
</slide>
</slideshow>
"""

dom = xml.dom.minidom.parseString(document)

def getText(nodelist):
    rc = []
    for node in nodelist:
        if node.nodeType == node.TEXT_NODE:
            rc.append(node.data)
    return ''.join(rc)

def handleSlideshow(slideshow):
    print("<html>")
    handleSlideshowTitle(slideshow.getElementsByTagName("title")[0])
    slides = slideshow.getElementsByTagName("slide")
    handleToc(slides)
    handleSlides(slides)
    print("</html>")

def handleSlides(slides):
    for slide in slides:
        handleSlide(slide)

def handleSlide(slide):
    handleSlideTitle(slide.getElementsByTagName("title")[0])
    handlePoints(slide.getElementsByTagName("point"))

def handleSlideshowTitle(title):
    print(f"<title>{getText(title.childNodes)}</title>")

def handleSlideTitle(title):
    print(f"<h2>{getText(title.childNodes)}</h2>")

def handlePoints(points):
    print("<ul>")
    for point in points:
        handlePoint(point)
    print("</ul>")

def handlePoint(point):
    print(f"<li>{getText(point.childNodes)}</li>")

def handleToc(slides):
    for slide in slides:
        title = slide.getElementsByTagName("title")[0]
        print(f"<p>{getText(title.childNodes)}</p>")

handleSlideshow(dom)

minidom и стандарт DOM

Модуль xml.dom.minidom - это, по сути, DOM 1.0-совместимый DOM с некоторыми возможностями DOM 2 (в первую очередь, с возможностями пространства имен).

Использование интерфейса DOM в Python очень простое. Применяются следующие правила отображения:

  • Доступ к интерфейсам осуществляется через объекты экземпляра. Приложениям не следует инстанцировать классы самостоятельно; они должны использовать функции создателя, доступные на объекте Document. Производные интерфейсы поддерживают все операции (и атрибуты) из базовых интерфейсов, а также любые новые операции.

  • Операции используются как методы. Поскольку DOM использует только in параметров, аргументы передаются в обычном порядке (слева направо). Необязательных аргументов нет. Операции void возвращают None.

  • Атрибуты IDL отображаются на атрибуты экземпляра. Для совместимости с отображением языка OMG IDL для Python, к атрибуту foo можно также получить доступ через методы-акселераторы _get_foo() и _set_foo(). Атрибуты readonly не должны изменяться; во время выполнения это не соблюдается.

  • Типы short int, unsigned int, unsigned long long и boolean - все они отображаются на целочисленные объекты Python.

  • Тип DOMString соответствует строкам Python. xml.dom.minidom поддерживает либо байты, либо строки, но обычно выдает строки. Значения типа DOMString также могут быть None, если спецификация DOM от W3C разрешает им иметь значение IDL null.

  • Объявления const отображаются на переменные в соответствующей области видимости (например, xml.dom.minidom.Node.PROCESSING_INSTRUCTION_NODE); их нельзя изменять.

  • В настоящее время DOMException не поддерживается в xml.dom.minidom. Вместо этого в xml.dom.minidom используются стандартные исключения Python, такие как TypeError и AttributeError.

  • Объекты NodeList реализуются с помощью встроенного в Python типа списка. Эти объекты предоставляют интерфейс, определенный в спецификации DOM, но в ранних версиях Python они не поддерживают официальный API. Тем не менее, они гораздо более «питоновские», чем интерфейс, определенный в рекомендациях W3C.

Следующие интерфейсы не имеют реализации в xml.dom.minidom:

  • DOMTimeStamp

  • EntityReference

Большинство из них отражают информацию в XML-документе, которая не является общеполезной для большинства пользователей DOM.

Сноски