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

Исходный код: Lib/xml/dom/minidom.py.


xml.dom.minidom - это минимальная реализация интерфейса Document Object Model, с 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')  # 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 результатом является строка Unicode, а объявление 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("<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)

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.

Сноски

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.

Вернуться на верх