Как правильно обходить и искать данные в XML при использовании ЧтениеXML и DOM

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Работа с XML-файлами в 1С — это базовая задача при обмене данными, однако наличие пространств имен (префиксов вроде d7p1:) и атрибутов часто ставит разработчиков в тупик. Рассмотрим ситуацию, когда нам нужно найти конкретный узел по значению его атрибута и получить содержимое этого узла. Мы разберем несколько подходов: от потокового чтения до объектной модели DOM и использования XPath.

Анализ проблемы: почему ЧтениеXML не находит атрибут

Разберем типичную ошибку. В исходном сообщении автор пытается найти узел, используя метод ПолучитьАтрибут("Характеристика"). Проанализируем структуру узла:

<d7p1:Property name="Характеристика">

Здесь Property — это имя узла (с префиксом d7p1), name — это имя атрибута, а "Характеристика" — это значение атрибута. Ошибка заключается в том, что метод ПолучитьАтрибут() ожидает в качестве параметра имя атрибута (в нашем случае "name"), а не его значение.

Также важно помнить, что объект ЧтениеXML работает как последовательный поток. Когда метод Прочитать() возвращает Истина, указатель перемещается на следующий фрагмент файла (начало элемента, текст, конец элемента). Атрибуты доступны только тогда, когда ТипУзла установлен в НачалоЭлемента.

Решение №1: Правильное использование ЧтениеXML (потоковый метод)

Если файл имеет огромный размер, использование ЧтениеXML является наиболее оптимальным с точки зрения потребления оперативной памяти. Выясним, как правильно организовать обход, чтобы найти нужное свойство. Проанализируем следующий алгоритм:

  1. Используем свойство ЛокальноеИмя, чтобы игнорировать префиксы пространств имен.
  2. Проверяем тип узла — нам нужно только начало элемента.
  3. Перебираем атрибуты или обращаемся к ним по имени.

Посмотрим на пример кода, реализующего этот подход:


ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(СтрокаХМЛ);

Пока ЧтениеXML.Прочитать() Цикл
    // Проверяем, что мы находимся в начале элемента Property
    Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента 
        И ЧтениеXML.ЛокальноеИмя = "Property" Тогда
        
        // Получаем значение атрибута "name"
        ЗначениеАтрибута = ЧтениеXML.ПолучитьАтрибут("name");
        
        Если ЗначениеАтрибута = "Характеристика" Тогда
            // Теперь нам нужно прочитать значение внутри тега
            // Метод ПрочитатьСодержимоеКакСтроку автоматически перейдет к концу узла
            Результат = ЧтениеXML.ПрочитатьСодержимоеКакСтроку();
            Сообщить("Найдено значение: " + Результат);
        КонецЕсли;
        
    КонецЕсли;
КонецЦикла;

Важный нюанс: использование ЛокальноеИмя позволяет нам находить узлы Property, даже если в разных частях файла они имеют разные префиксы (например, d7p1 или d8p2). Это делает код более универсальным.

Решение №2: Использование объектной модели DOM

Если размер XML-файла позволяет (до нескольких десятков мегабайт), удобнее использовать ПостроительDOM. Это позволит нам не писать сложные циклы, а работать с документом как с деревом объектов. Разберем, как найти нужный элемент через DOM.

В сообщениях форума была попытка использовать ПолучитьЭлементыПоИмени(). Проанализируем правильный способ работы с этим методом:


ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(СтрокаХМЛ);

ПостроительDOM = Новый ПостроительDOM;
ДокументDOM = ПостроительDOM.Прочитать(ЧтениеXML);

// Ищем все элементы с локальным именем Property (без учета префиксов)
СписокУзлов = ДокументDOM.ПолучитьЭлементыПоИмени("Property");

Для Каждого Узел Из СписокУзлов Цикл
    // Проверяем наличие и значение атрибута name
    АтрибутName = Узел.Атрибуты.ПолучитьИмя("name");
    Если АтрибутName <> Неопределено И АтрибутName.Значение = "Характеристика" Тогда
        // Текстовое значение находится внутри дочернего узла Value
        УзлыЗначений = Узел.ПолучитьЭлементыПоИмени("Value");
        Если УзлыЗначений.Количество() > 0 Тогда
            Сообщить("Значение из DOM: " + УзлыЗначений[0].ТекстовоеСодержимое);
        КонецЕсли;
    КонецЕсли;
КонецЦикла;

Решение №3: Мощный XPath (универсальный поиск)

Самый элегантный и современный способ — использование языка запросов XPath. Он позволяет одной строкой найти нужный узел по любому критерию. Выясним, как обойти проблему пространств имен в XPath.

Обычно для работы с префиксами в 1С требуется РазыменовательПространствИменDOM. Однако существует «хитрый» способ составить запрос так, чтобы он игнорировал префиксы, используя функцию local-name(). Рассмотрим этот пример:


ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(СтрокаХМЛ);
ПостроительDOM = Новый ПостроительDOM;
ДокументDOM = ПостроительDOM.Прочитать(ЧтениеXML);

// XPath запрос: найти любой узел, у которого локальное имя "Property" 
// и атрибут "name" равен "Характеристика"
ЗапросXPath = "//*[local-name()='Property' and @name='Характеристика']";

РезультатXPath = ДокументDOM.ВычислитьВыражениеXPath(ЗапросXPath);
Узел = РезультатXPath.ПолучитьСледующий();

Пока Узел <> Неопределено Цикл
    // Выводим текстовое содержимое узла или его дочерних элементов
    Сообщить("Найдено через XPath: " + Узел.ТекстовоеСодержимое);
    Узел = РезультатXPath.ПолучитьСледующий();
КонецЦикла;

Этот подход является наиболее надежным, так как он не зависит от структуры вложенности и конкретных префиксов xmlns, которые могут меняться при выгрузке из разных систем. Для более детального изучения возможностей таких запросов подойдёт руководство по практической работе с XPath в 1С.

Решение №4: Сериализатор XDTO

Если ваш XML соответствует структуре объектов 1С (как в примере автора, где видны ссылки на перечисления EnumRef.ВидыДеятельности), можно попробовать использовать СериализаторXDTO. Это превратит XML сразу в объектную модель 1С. Проанализируем ситуацию:


ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(СтрокаXML);

// Пытаемся прочитать данные напрямую в типы 1С
Попытка
    Данные = СериализаторXDTO.ПрочитатьXML(ЧтениеXML);
    // Если XML содержит описание структуры XDTO, мы получим объект
    // к которому можно обращаться через точку: Данные.Характеристика
Исключение
    Сообщить("XDTO не смог распознать структуру автоматически: " + ОписаниеОшибки());
КонецПопытки;

Подведем итог: для простых задач и небольших файлов используйте DOM + XPath с функцией local-name() — это сэкономит вам массу времени на написании циклов. Для высокопроизводительных систем и больших файлов оставайтесь на ЧтениеXML, не забывая проверять ТипУзла и использовать ЛокальноеИмя.

← На главную