# `xml.dom.minidom` — Minimal DOM implementation

Вихідний код: Lib/xml/dom/minidom.py

`xml.dom.minidom` — це мінімальна реалізація інтерфейсу об’єктної моделі документа з 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_or_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». Ви можете отримати цей об’єкт, викликавши функцію `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"

When you are finished with a DOM tree, you may optionally call the `unlink()` method to encourage early cleanup of the now-unneeded objects. `unlink()` is an `xml.dom.minidom`-specific extension to the DOM API that renders the node and its descendants essentially useless. Otherwise, Python’s garbage collector will eventually take care of the objects in the tree.

Дивись також

Специфікація рівня 1 об’єктної моделі документа (DOM)

  Рекомендація W3C щодо DOM, що підтримується `xml.dom.minidom`.

## Об’єкти DOM

Визначення DOM API для Python надається як частина документації модуля `xml.dom`. У цьому розділі наведено відмінності між API та `xml.dom.minidom`.

Node.unlink()

  Розбийте внутрішні посилання в DOM, щоб у версіях Python без циклічного збирання сміття було зібрано сміття. Навіть якщо циклічний 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 в об’єкт запису. Записувач отримує тексти, але не байти як вхідні дані, він повинен мати метод `write()`, який відповідає методу інтерфейсу файлового об’єкта. Параметр indent — це відступ поточного вузла. Параметр addindent є поступовим відступом для використання підвузлів поточного. Параметр newl визначає рядок, який використовуватиметься для завершення символів нового рядка.

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

  Similarly, explicitly stating the standalone argument causes the standalone document declarations to be added to the prologue of the XML document. If the value is set to `True`, `standalone="yes"` is added, otherwise it is set to `"no"`. Not stating the argument will omit the declaration from the document.

  Змінено в версії 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(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-сумісним 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.