print ". "*level, node.nodeValue.strip()
else: # ELEMENT_NODE или DOCUMENT_NODE
atts = node.attributes or {}
att_string = ", ".join(
["%s=%s " % (k, v) for k, v in atts.items()])
print ". "*level, node.nodeName, att_string
for child in node.childNodes:
output_tree(child, level+1)
output_tree(dom)
В этом примере дерево выводится с помощью определенной функции output_tree()
, которая принимает на входе узел и вызывается рекурсивно для всех вложенных узлов.
В результате получается примерно следующее:
#document
. expression
. . operation type=+
. . . operand
. . . . 2
. . . operand
. . . . operation type=*
. . . . . operand
. . . . . . 3
. . . . . operand
. . . . . . 4
Здесь же применяется метод normalize()
для того, чтобы все текстовые фрагменты были слиты воедино (в противном случае может следовать подряд несколько узлов с текстом).
Можно заметить, что даже в небольшом примере использовались атрибуты узлов: node.nodeType
указывает тип узла, node.nodeValue
применяется для доступа к данным, node.nodeName
дает имя узла (соответствует названию тега), node.attributes
дает доступ к атрибутам узла. node.childNodes
применяется для доступа к дочерним узлам. Этих свойств достаточно, чтобы рекурсивно обойти дерево.
Все узлы являются экземплярами подклассов класса Node
. Они могут быть следующих типов:
Название | Описание | Метод для создания |
---|---|---|
ELEMENT_NODE | Элемент | createElement(tagname) |
ATTRIBUTE_NODE | Атрибут | createAttribute(name) |
TEXT_NODE | Текстовый узел | createTextNode(data) |
CDATA_SECTION_NODE | Раздел | CDATA |
ENTITY_REFERENCE_NODE | Ссылка на сущность | |
ENTITY_NODE | Сущность | |
PROCESSING_INSTRUCTION_NODE | Инструкция по обработке | createProcessingInstruction(target, data) |
COMMENT_NODE | Комментарий | createComment(comment) |
DOCUMENT_NODE | Документ | |
DOCUMENT_TYPE_NODE | Тип документа | |
DOCUMENT_FRAGMENT_NODE | Фрагмент документа | |
NOTATION_NODE | Нотация |
В DOM документ является деревом, в узлах которого стоят объекты нескольких возможных типов. Узлы могут иметь атрибуты или данные. Доступ к узлам можно осуществлять через атрибуты вроде childNodes
(дочерние узлы), firstChild
(первый дочерний узел), lastChild
(последний дочерний узел), parentNode
(родитель), nextSibling
(следующий брат), previousSibling
(предыдущий брат).
Выше уже говорилось о методе appendChild()
. К нему можно добавить методы insertBefore(newChild, refChild)
(вставить newChild
до refChild
), removeChild(oldChild)
(удалить дочерний узел), replaceChild(newChild, oldChild)
(заметить oldChild
на newChild
). Есть еще метод cloneNode(deep)
, который клонирует узел (вместе с дочерними узлами, если задан deep=1
).
Узел типа ELEMENT_NODE
, помимо перечисленных методов «просто» узла, имеет много других методов. Вот основные из них:
tagName
Имя типа элемента.
getElementsByTagName(tagname)
Получает элементы с указанным именем tagname
среди всех потомков данного элемента.
getAttribute(attname)
Получить значение атрибута с именем attname
.
getAttributeNode(attrname)
Возвращает атрибут с именем attrname
в виде объекта–узла.
removeAttribute(attname)
Удалить атрибут с именем attname
.
removeAttributeNode(oldAttr)
Удалить атрибут oldAttr
(задан в виде объекта–узла).
setAttribute(attname, value)
Устанавливает значение атрибута attname
равным строке value
.
setAttributeNode(newAttr)
Добавляет новый узел–атрибут к элементу. Старый атрибут заменяется, если имеет то же имя.
Здесь стоит заметить, что атрибуты в рамках элемента повторяться не должны. Их порядок также не важен с точки зрения информационной модели XML.
В качестве упражнения предлагается составить функцию, которая будет вычислять значение выражения, заданного в XML–представлении.