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


xml.dom.minidom — это минимальная реализация интерфейса объектной модели документа с API, аналогичным 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')  # парсинг XML-файла по имени

datasource = open('c:\\temp\\mydata.xml')
dom2 = parse(datasource)  # парсинг открытого файла

dom3 = parseString('<myxml>Некоторые данные<empty/> ещё немного данных</myxml>')

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

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

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

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

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

Возвращает Document, который представляет string. Данный метод создаёт объект io.StringIO для строки и передаёт его parse().

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

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

Вы также можете создать Document, вызвав метод объекта «Реализация DOM». Вы можете получить данный объект, либо вызвав функцию 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>Некоторые данные</myxml>")
assert dom3.documentElement.tagName == "myxml"

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

См.также

Спецификация объектной модели документа (DOM) Уровня 1
Рекомендация W3C для DOM, поддерживаемая xml.dom.minidom.

DOM-объекты

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

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

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

with xml.dom.minidom.parse(datasource) as dom:
    ... # Работа с dom.
Node.writexml(writer, indent="", addindent="", newl="")

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

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

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

Node.toxml(encoding=None)

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

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

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

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

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

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

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

Пример 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("<title>%s</title>" % getText(title.childNodes))

def handleSlideTitle(title):
    print("<h2>%s</h2>" % getText(title.childNodes))

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

def handlePoint(point):
    print("<li>%s</li>" % getText(point.childNodes))

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

handleSlideshow(dom)

Минидом и стандарт DOM

Модуль xml.dom.minidom по сути является DOM-совместимым DOM 1.0 с некоторыми функциями 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, если разрешено иметь значение IDL null спецификацией DOM от W3C.
  • Объявления 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 пользователей.

Сноски

[1]Включённое в XML вывод имя кодировки, должно соответствовать соответствующим стандартам. Например, «UTF-8» допустим, но «UTF8» недействителен в объявлении XML-документа даже не смотря на то, что Python принимает данное имя кодировки. См. https://www.w3.org/TR/2006/REC-xml11-20060816/#NT-EncodingDecl и https://www.iana.org/assignments/character-sets/character-sets.xhtml.