Чем отличается Узел (Node) от Элемента (Element) в XML при работе с V7PLUS.DLL в 1С?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

При работе с XML-файлами в среде 1С:Предприятие (особенно в версии 7.7 с использованием внешней компоненты V7PLUS.DLL или встроенного объекта XMLParser) разработчики часто сталкиваются с терминами «Узел» (Node) и «Элемент» (Element). На первый взгляд может показаться, что это синонимы, поскольку в литературе и коде эти слова постоянно сменяют друг друга. Однако смешивание этих понятий приводит к трудночитаемому коду и регулярным ошибкам времени выполнения (runtime errors).

Выясним причину этой путаницы, разберем по шагам теоретическую базу объектной модели DOM (Document Object Model), проанализируем разницу в свойствах и методах компоненты V7PLUS.DLL и научимся правильно обрабатывать данные без досадных сбоев программы.

1. Объектная модель DOM и наследование классов

Главная причина непонимания разницы между Узлом и Элементом кроется в объектно-ориентированной архитектуре XML-парсеров. В соответствии со спецификацией W3C XML DOM, весь XML-документ представляется в виде древовидной структуры, состоящей из объектов. Отношения между Узлом и Элементом строятся на базовом принципе ООП — наследовании.

Узел (Node / IXMLDOMNode) — это самый базовый, абстрактный класс (интерфейс) для абсолютно любого объекта внутри XML-документа. Всё, что парсер считывает из файла и помещает в оперативную память, является Узлом. Узлом является сам документ, строка комментария, служебное объявление в начале файла, имя тега, текст внутри этого тега и его атрибуты.

Элемент (Element / IXMLDOMElement) — это специализированный класс-наследник, который расширяет возможности базового Узла. Элементом в XML называют исключительно полноценную структурную единицу — тег (открывающий, закрывающий или самозакрывающийся).

Сформулируем главное правило, которое необходимо навсегда уяснить для правильного построения архитектуры в своей голове: любой Элемент гарантированно является Узлом, но далеко не каждый Узел является Элементом.

2. Разбор структуры XML на конкретном примере

Рассмотрим подробнее небольшой фрагмент XML-кода, чтобы наглядно увидеть, где здесь скрываются узлы, а где элементы. Проанализируем следующую структуру:


<!-- Список бытовой техники -->
<Товар Код="00015">Холодильник</Товар>

Для человеческого глаза здесь видна всего одна строка с товаром и один комментарий. Однако XML-парсер, строящий DOM-дерево в памяти компьютера, разобьет этот фрагмент сразу на пять различных объектов-узлов, и лишь один из них получит статус Элемента:

  1. Узел комментария: Текст <!-- Список бытовой техники --> преобразуется в узел с типом Comment. Это узел, но он не является элементом. У него нет имени тега или атрибутов.
  2. Узел элемента: Конструкция <Товар ...></Товар> преобразуется в объект с типом Element. Это единственный полноправный Элемент в данном фрагменте. У него есть имя тега (Товар), и только у него мы можем запрашивать атрибуты или вложенную структуру.
  3. Узел атрибута: Фрагмент Код="00015" преобразуется в узел типа Attribute. Он прочно связан с родительским элементом Товар, но в иерархии дерева DOM он позиционируется как отдельный узел.
  4. Текстовый узел: Слово Холодильник преобразуется в узел типа Text. Крайне важно понять: сам элемент Товар непосредственно в себе этот текст не хранит! Текст является вложенным («дочерним») объектом по отношению к элементу. Элемент хранит ссылку на подчиненный текстовый узел.
  5. Узел пробельных символов (Whitespace): Символы перевода строки и пробелы (отступы) между комментарием и тегом <Товар> парсер также может прочитать и представить в виде служебного текстового узла разметки.

3. Простая бытовая аналогия

Если вам все еще трудно разложить эти понятия «по полочкам», воспользуемся аналогией из мира техники:

Попытка вызвать специфический метод элемента (например, получение атрибута) у текстового узла — это то же самое, что попытка установить мобильное приложение на обычный медный провод. И провод, и смартфон — это устройства (узлы), но приложение может работать только на смартфоне (элементе).

4. Различия на уровне свойств и методов в V7PLUS.DLL

Давайте посмотрим на проблему глазами разработчика 1С 7.7. Когда мы загружаем внешнюю компоненту V7PLUS.DLL и создаем объект XMLParser, мы оперируем интерфейсами Microsoft MSXML. Посмотрим, какими методами обладают эти сущности.

Базовый объект УЗЕЛ (Node) предоставляет методы для навигации по иерархии:

Объект ЭЛЕМЕНТ (Element) добавляет к этому списку уникальные функции для работы с атрибутами:

5. Разбор типичной ошибки разработчика в 1С 7.7

Рассмотрим ситуацию, в которой программисты чаще всего совершают ошибку, путая узел и элемент. Представим, что нам необходимо прочитать значения атрибутов у всех подчиненных элементов внутри некоторого контейнера. Напишем код, который кажется логичным на первый взгляд:


// ОШИБОЧНЫЙ ВАРИАНТ КОДА!
РодительскийУзел = СозданныйДокументXML.ВыбратьУзел("Данные");
Количество = РодительскийУзел.КоличествоПодчиненных();

Для Индекс = 1 По Количество Цикл
    ТекущийУзел = РодительскийУзел.ПолучитьПодчиненныйПоНомеру(Индекс);
    // Попытка прочитать атрибут
    ЗначениеАтрибута = ТекущийУзел.ПолучитьАтрибут("Код"); 
    Сообщить(ЗначениеАтрибута);
КонецЦикла;

При выполнении этого кода программа на определенной итерации цикла с высокой долей вероятности аварийно завершится с ошибкой: «Поле агрегатного объекта не обнаружено (ПолучитьАтрибут)». Почему это происходит?

Дело в том, что метод КоличествоПодчиненных() считает абсолютно все узлы: и теги-элементы, и скрытые текстовые узлы (переводы строк, пробелы оформления), и комментарии. Метод ПолучитьПодчиненныйПоНомеру() послушно возвращает очередной узел. Если нам попался текстовый узел или комментарий, у него физически отсутствует метод ПолучитьАтрибут(), ведь текстовый фрагмент не может содержать атрибутов!

Чтобы избежать подобных ошибок, мы должны применить проверку типа узла перед вызовом специфических методов элемента. Напишем корректный и безопасный вариант кода:


// ПРАВИЛЬНЫЙ И БЕЗОПАСНЫЙ ВАРИАНТ КОДА
РодительскийУзел = СозданныйДокументXML.ВыбратьУзел("Данные");
Количество = РодительскийУзел.КоличествоПодчиненных();

Для Индекс = 1 По Количество Цикл
    ТекущийУзел = РодительскийУзел.ПолучитьПодчиненныйПоНомеру(Индекс);
    
    // Проверяем тип узла. Нам нужны только узлы-элементы (тип 1)
    Если (ТекущийУзел.Тип = 1) Или (ВРег(ТекущийУзел.ТипСтрока) = "ELEMENT") Тогда
        // Теперь мы на 100% уверены, что ТекущийУзел является Элементом
        ЗначениеАтрибута = ТекущийУзел.ПолучитьАтрибут("Код");
        Сообщить("Успешно прочитан код элемента: " + ЗначениеАтрибута);
    Иначе
        // Игнорируем текстовые узлы, комментарии и пробелы разметки
        Сообщить("Пропущен служебный узел типа: " + ТекущийУзел.ТипСтрока);
    КонецЕсли;
КонецЦикла;

Таким образом, понимание разницы между абстрактным «Узлом» и конкретным «Элементом» гарантирует стабильную работу ваших обработок импорта и экспорта данных. Четкое разграничение этих понятий защитит код от случайных сбоев при изменении форматирования XML-файлов (например, при появлении лишних пробелов или комментариев в структуре).

← На главную