Как реализовать универсальное чтение любого XML-файла в 1С без привязки к именам полей?

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

Часто перед разработчиком стоит задача прочитать XML-файл, структура которого заранее неизвестна или может меняться. Стандартный подход с жестким перебором имен узлов через условия Если ЧтениеXML.Имя = "Товар" Тогда крайне неудобен, так как требует переписывания кода под каждый новый формат. В этой статье мы разберем, как создать универсальный механизм, который сможет «переварить» любой XML и представить его в удобном для 1С виде, например, в дереве значений или применив XSLT-визуализацию для создания красивого отчета.

Проблема «жесткого» кодирования и путь к универсальности

Типичная ошибка новичка — пытаться угадать структуру файла. Рассмотрим пример кода, который часто встречается на форумах и который не является универсальным:


ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(ПутьКФайлу);
Пока ЧтениеXML.Прочитать() Цикл
    Если ЧтениеXML.Имя = "Nomenclatura" И ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
        // Здесь начинается жесткая привязка к полям
        ИмяТекущегоУзла = "Nomenclatura";
    КонецЕсли;
КонецЦикла;

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

Метод 1: Последовательное чтение с использованием рекурсии

Это самый эффективный метод для работы с файлами любого объема. Мы будем использовать объект ЧтениеXML и рекурсивную функцию. Суть заключается в том, что программа «идет» по дереву XML и для каждого найденного элемента создает строку в ДеревоЗначений.

Рассмотрим пример реализации такой функции. Мы не будем проверять конкретные имена, а будем брать свойство Имя самого узла:


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

В данном примере мы используем объект ДеревоЗначений с колонками ИмяУзла, ЗначениеУзла и Атрибуты. Такой подход позволяет визуализировать структуру любого XML в виде иерархии прямо на форме 1С.

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

Если файл имеет разумный объем (до 50-100 Мб), мы можем воспользоваться моделью DOM (Document Object Model). Этот метод позволяет загрузить весь XML в память и обращаться к его узлам как к объектам. Выясним преимущества этого способа: нам не нужно следить за курсором чтения, мы можем перемещаться по дереву вверх и вниз.

Разберем по шагам работу с ПостроительDOM:

  1. Создаем объект ЧтениеXML и открываем файл.
  2. Создаем ПостроительDOM и получаем ДокументDOM.
  3. Используем рекурсивный обход коллекции ДочерниеУзлы.

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

// Теперь можно получить коренной элемент и начать обход
КорневойУзел = ДокументDOM.ЭлементДокумента;
Для Каждого Узел Из КорневойУзел.ДочерниеУзлы Цикл
    // Обработка узла
КонецЦикла;

Важный момент: использование DOM требует значительных ресурсов оперативной памяти. Для «любых» (в том числе гигантских) файлов этот метод может не подойти, вызвав ошибку нехватки памяти.

Метод 3: Использование Фабрики XDTO

Метод ПрочитатьXML глобальной фабрики XDTO — это, пожалуй, самый «красивый» способ в 1С. Если у вас нет схемы XSD, 1С попытается прочитать XML в объект типа ОбъектXDTO с динамическим типом данных. Проанализируем, как это работает:


ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(ПутьКФайлу);
Данные = ФабрикаXDTO.ПрочитатьXML(ЧтениеXML);
// Теперь 'Данные' — это объект XDTO, свойства которого соответствуют тегам XML

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

Метод 4: Готовые функции БСП

Если ваша конфигурация основана на «Библиотеке стандартных подсистем» (БСП), вам не нужно изобретать велосипед. Проанализируем возможности общего модуля ОбщегоНазначения. В нем часто присутствует функция ПрочитатьXMLВСтруктуру или аналогичные инструменты для работы с XDTO. Чтобы быстро находить нужные команды в библиотеке, разработчикам полезно иметь под рукой справочник по методам БСП с примерами использования.

Использование БСП гарантирует, что код будет соответствовать стандартам 1С и корректно обрабатывать различные кодировки и спецсимволы.

Вывод данных в табличный документ

Чтобы выполнить пожелание пользователя и вывести результат в ТабличныйДокумент, мы рекомендуем использовать алгоритм рекурсивного обхода дерева значений. .

Алгоритм вывода в таблицу:

  1. Сначала прочитайте XML в ДеревоЗначений (Метод 1).
  2. Затем рекурсивно обойдите дерево значений.
  3. Для каждого уровня вложенности устанавливайте отступ в табличном документе.
  4. Используйте метод Область.Текст для вывода имени узла в первую колонку, а значения — во вторую.

Посмотрим на пример вывода:


Процедура ВывестиДеревоВТабДок(СтрокиДерева, ТабДок, Уровень = 0)
    Для Каждого Строка Из СтрокиДерева Цикл
        Область = ТабДок.ПолучитьОбласть("R" + Формат(ТабДок.ВысотаТаблицы + 1, "ЧГ=0") + "C1:C2");
        Область.Область(1, 1).Текст = Строка.ИмяУзла;
        Область.Область(1, 1).Отступ = Уровень;
        Область.Область(1, 2).Текст = Строка.ЗначениеУзла;
        ТабДок.Вывести(Область);
        
        // Рекурсия для дочерних элементов
        ВывестиДеревоВТабДок(Строка.Строки, ТабДок, Уровень + 1);
    КонецЦикла;
КонецПроцедуры

Дополнительные советы для профессионалов

При работе с действительно универсальным чтением не забудьте про пространства имен (Namespaces). Если файл содержит префиксы вроде <v8:item>, стандартное свойство Имя вернет строку "v8:item". Чтобы получить чистые данные, используйте свойство ЛокальноеИмя.

Также помним про атрибуты. В XML данные могут храниться как внутри тегов, так и в их атрибутах (например, <Item ID="001">). Универсальный парсер обязан проверять ЧтениеXML.КоличествоАтрибутов() и считывать их через ЧтениеXML.ЗначениеАтрибута(), иначе часть информации будет потеряна.

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

← На главную