К статье
Практический материал

Разработка и Конфигурация · 13 мин

Как в 1С прочитать XML в структуру

Как в 1С прочитать XML-документ в структуру: ЧтениеXML, фабрика XDTO и сериализация. Примеры кода, проверка результата, типичные ошибки и FAQ.

Задача прочитать XML-документ и получить его содержимое в виде структуры возникает при обмене данными с внешними системами: загрузке прайсов, выгрузках из банков, ответах веб-сервисов, файлах ЭДО. В 1С нет одной встроенной функции «XML в структуру», поэтому используют три рабочих подхода: построчное чтение через объект ЧтениеXML, разбор через фабрику XDTO и универсальную рекурсивную функцию-парсер.

Выбор способа зависит от формата файла. Если XML соответствует известной схеме (XSD), удобнее фабрика XDTO — она сама контролирует типы. Если структура файла заранее неизвестна или меняется, надёжнее последовательное чтение потока. Для простых «плоских» файлов часто достаточно короткой рекурсивной функции, которая складывает элементы и атрибуты в структуру или соответствие.

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

Какой способ чтения XML выбрать

В платформе 1С есть несколько механизмов работы с XML, и они не взаимозаменяемы полностью. Объект ЧтениеXML читает документ последовательно, узел за узлом, и подходит для файлов любого размера, включая очень большие выгрузки, потому что не загружает всё в память. Объект ЧтениеXML требует ручной обработки каждого типа узла: начало элемента, текст, конец элемента.

Фабрика XDTO работает иначе: вы описываете схему (или используете существующий пакет XDTO конфигурации), а платформа сама превращает XML в объектное дерево с типизированными полями. Это удобно для стандартных форматов — например, файлов обмена с банками или государственными сервисами, где схема зафиксирована документацией.

Третий вариант — быстрая сериализация через ПрочитатьXML, но она работает только для объектов, которые платформа умеет сериализовать сама (данные, записанные функцией ЗаписатьXML). Для произвольного внешнего XML этот способ не подходит.

Сравнение подходов

ЧтениеXML + рекурсия

Подходит для любого XML без схемы, контроль памяти, но нужно писать обработчик узлов вручную

Фабрика XDTO

Подходит при известной схеме XSD; типы проверяются автоматически, но схему нужно создать или импортировать

ПрочитатьXML / ЗаписатьXML

Только для данных, ранее сериализованных самой платформой; для внешних файлов не работает

Чтение XML через ЧтениеXML: базовый цикл

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

Минимальный каркас чтения файла выглядит так:

Чтение = Новый ЧтениеXML;

Чтение.ОткрытьФайл(ПутьКФайлу);

Пока Чтение.Прочитать() Цикл

Если Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда

ИмяУзла = Чтение.Имя;

// обработка начала элемента

ИначеЕсли Чтение.ТипУзла = ТипУзлаXML.Текст Тогда

ЗначениеУзла = Чтение.Значение;

// обработка текста

ИначеЕсли Чтение.ТипУзла = ТипУзлаXML.КонецЭлемента Тогда

// закрытие элемента

КонецЕсли;

КонецЦикла;

Чтение.Закрыть();

Атрибуты текущего элемента читаются через Чтение.КоличествоАтрибутов(), Чтение.ИмяАтрибута(Индекс) и Чтение.ЗначениеАтрибута(Индекс) либо по имени: Чтение.ЗначениеАтрибута("Код"). Важно читать атрибуты сразу, пока текущий узел — начало элемента: после перехода к следующему узлу они станут недоступны.

Если файл небольшой, его можно прочитать целиком в строку и открыть через Чтение.УстановитьСтроку(ТекстXML). Для больших файлов используйте только файловый режим — загрузка гигабайтной выгрузки в строку приведёт к перерасходу памяти сервера.

Универсальная функция разбора XML в структуру

Для файлов без фиксированной схемы удобна рекурсивная функция: она проходит по узлам и складывает данные в структуру. Повторяющиеся одноимённые элементы (например, строки табличной части) правильнее собирать в массив, поэтому многие разработчики используют Соответствие вместо Структура — в соответствии ключи могут быть любыми строками, включая имена с дефисами и двоеточиями, недопустимые для ключей структуры.

Функция XMLВСоответствие(Чтение, ИмяКорня)

Результат = Новый Соответствие;

Пока Чтение.Прочитать() Цикл

Если Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда

Имя = Чтение.Имя;

// собираем атрибуты

Атрибуты = Новый Соответствие;

Для Инд = 0 По Чтение.КоличествоАтрибутов() - 1 Цикл

Атрибуты.Вставить(Чтение.ИмяАтрибута(Инд),

Чтение.ЗначениеАтрибута(Инд));

КонецЦикла;

Вложенный = XMLВСоответствие(Чтение, Имя);

Если Атрибуты.Количество() > 0 Тогда

Вложенный.Вставить("_Атрибуты", Атрибуты);

КонецЕсли;

// одноимённые элементы собираем в массив

Текущее = Результат[Имя];

Если Текущее = Неопределено Тогда

Результат.Вставить(Имя, Вложенный);

Иначе

Если ТипЗнч(Текущее) <> Тип("Массив") Тогда

Массив = Новый Массив;

Массив.Добавить(Текущее);

Результат.Вставить(Имя, Массив);

Текущее = Массив;

КонецЕсли;

Текущее.Добавить(Вложенный);

КонецЕсли;

ИначеЕсли Чтение.ТипУзла = ТипУзлаXML.Текст Тогда

Результат.Вставить("_Текст", СокрЛП(Чтение.Значение));

ИначеЕсли Чтение.ТипУзла = ТипУзлаXML.КонецЭлемента Тогда

Прервать;

КонецЕсли;

КонецЦикла;

Возврат Результат;

КонецФункции

Вызов функции начинается после открытия файла и первого чтения корневого элемента. В результате получается дерево соответствий, где текстовое содержимое лежит в ключе _Текст, а атрибуты — в ключе _Атрибуты. Такое соглашение об именах служебных ключей можно менять под свой проект, главное — чтобы оно не пересекалось с реальными именами элементов.

Если вы точно знаете, что имена элементов — допустимые идентификаторы (без дефисов, пробелов, цифр в начале), соответствие можно заменить структурой. Но для произвольных внешних файлов структура рискованна: попытка вставить ключ ns:item или item-code вызовет ошибку.

Чтение через фабрику XDTO

Когда схема XML известна, фабрика XDTO избавляет от ручного разбора. Схему добавляют в конфигурацию как пакет XDTO или загружают из XSD-файла программно. После этого файл читается в объектное дерево, где к полям обращаются по именам, а типы (числа, даты, булево) уже приведены.

Чтение = Новый ЧтениеXML;

Чтение.ОткрытьФайл(ПутьКФайлу);

ОбъектXDTO = ФабрикаXDTO.ПрочитатьXML(Чтение);

Чтение.Закрыть();

// обращение к данным

Для Каждого СтрокаТовара Из ОбъектXDTO.Товары.Товар Цикл

Наименование = СтрокаТовара.Наименование;

Цена = СтрокаТовара.Цена;

КонецЦикла;

Для разовой задачи без изменения конфигурации можно создать фабрику из файла схемы программно:

ЧтениеСхемы = Новый ЧтениеXML;

ЧтениеСхемы.ОткрытьФайл(ПутьКСхеме);

Построитель = Новый ПостроительDOM;

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

ПостроительСхем = Новый ПостроительСхемXML;

Схема = ПостроительСхем.СоздатьСхемуXML(ДокументDOM);

НаборСхем = Новый НаборСхемXML;

НаборСхем.Добавить(Схема);

МояФабрика = Новый ФабрикаXDTO(НаборСхем);

Если нужен именно универсальный разбор без схемы, фабрика тоже помогает: метод ПрочитатьXML без указания типа вернёт объект XDTO произвольной структуры, по которому можно пройтись через Свойства(). Это промежуточный вариант между ручным парсером и строгой схемой.

Порядок действий при чтении файла без схемы
  1. 1
    Откройте файл через ЧтениеXML.ОткрытьФайл() и убедитесь, что ошибок открытия нет
  2. 2
    Выполните первое Прочитать() и проверьте имя корневого элемента — оно должно совпадать с ожидаемым форматом
  3. 3
    Запустите рекурсивный разбор, собирая элементы в Соответствие, одноимённые — в Массив
  4. 4
    Проверьте на тестовом файле несколько известных значений: суммы, даты, количество строк
  5. 5
    Закройте ЧтениеXML и только после проверки данных запускайте запись в базу в транзакции

Проверка результата и контроль качества разбора

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

Обращайте внимание на три типовые ситуации. Первая: элемент, который в одних файлах один, а в других повторяется, — код должен одинаково обрабатывать и единичное значение, и массив. Вторая: пустые элементы без текста — в дереве они будут пустыми соответствиями, и обращение к _Текст вернёт Неопределено. Третья: числа и даты при ручном разборе остаются строками — их нужно приводить явно с учётом регионального формата (десятичный разделитель, порядок дня и месяца).

Для приведения типов используйте ОписаниеТипов и метод ПривестиЗначение() или функции Число(), Дата() с обработкой исключений — строка вида «1 234,56» в Число() не преобразуется без предварительной очистки от пробелов и замены запятой.

Контрольные точки

Как проверить результат разбора

Количество повторяющихся элементов
Норма

Совпадает с числом строк в исходном файле

Тревожный признак

Элементов меньше — одноимённые узлы затёрли друг друга

Значения с дробной частью
Норма

Числа приведены корректно, копейки не потеряны

Тревожный признак

Значения обрезаны или остались строками

Кириллица в тексте
Норма

Текст читается без иероглифов

Тревожный признак

Кракозябры — неверно определена кодировка файла

Атрибуты элементов
Норма

Все атрибуты попали в дерево

Тревожный признак

Атрибуты потеряны — читались после перехода к следующему узлу

Типичные ошибки и ограничения

Самая частая ошибка — потеря одноимённых элементов: если вставлять в структуру ключ без проверки существования, каждый следующий элемент с тем же именем перезапишет предыдущий, и из ста строк документа останется одна. Вторая по частоте проблема — кодировка: файл в windows-1251 без корректного объявления читается с искажением кириллицы. Кодировку можно задать явно при открытии: Чтение.ОткрытьФайл(Путь, , "windows-1251") варианта не существует, поэтому файл предварительно читают через ЧтениеТекста с нужной кодировкой и передают строку в УстановитьСтроку().

Третья группа ошибок связана с пространствами имён. Элементы с префиксами (ns:Документ) имеют локальное имя и URI пространства; сравнивать стоит Чтение.ЛокальноеИмя, а не полное Имя, иначе при смене префикса поставщиком код перестанет находить узлы. Также помните, что рекурсивный разбор очень глубоких документов теоретически ограничен стеком вызовов — для патологически вложенных файлов используйте итеративный вариант со стеком в массиве.

Типичные ошибки
01
Вставка одноимённых элементов в структуру без массива — предыдущие значения затираются
02
Использование Структуры для файлов с пространствами имён — ошибка на ключах с двоеточием и дефисом
03
Чтение атрибутов после перехода к следующему узлу — атрибуты уже недоступны
04
Сравнение по полному имени узла вместо ЛокальноеИмя — код ломается при смене префикса
05
Запись данных в базу без транзакции — при обрыве разбора остаются частично загруженные документы

Практические рекомендации по внедрению

Оформляйте разбор как отдельную функцию в общем модуле с явным входом (путь к файлу или строка XML) и выходом (соответствие или типизированная структура). Это позволит покрыть функцию тестами на эталонных файлах и переиспользовать в разных обменах. Файлы-примеры от контрагентов сохраняйте в каталог тестовых данных — форматы внешних систем меняются без предупреждения.

Запись результатов в базу выполняйте в транзакции и только после полной валидации дерева. Для регламентных загрузок добавьте журналирование: имя файла, число прочитанных элементов, число отклонённых строк с причинами. Это сильно упрощает разбор расхождений, когда поставщик данных утверждает, что «всё отправил правильно».

Если формат файла контролируете вы сами (обмен между двумя своими базами 1С), не изобретайте ручной разбор: сериализуйте данные через ЗаписатьXML/ЗначениеВСтрокуВнутр и читайте штатными средствами — это устраняет целый класс ошибок с типами и кодировками.
ПЗ
Практическое замечание

Частые вопросы

Можно ли прочитать XML напрямую в Структуру без Соответствия?

Можно, если вы уверены, что все имена элементов — допустимые идентификаторы: латиница или кириллица, без пробелов, дефисов и двоеточий, не начинаются с цифры. Для внешних файлов с пространствами имён это условие обычно не выполняется, поэтому безопаснее Соответствие.

Как прочитать XML, полученный строкой из HTTP-ответа?

Создайте ЧтениеXML и вызовите УстановитьСтроку(ТекстОтвета) вместо ОткрытьФайл(). Дальнейший разбор идентичен файловому. Если ответ в кодировке, отличной от UTF-8, сначала получите тело через ПолучитьТелоКакСтроку() с указанием кодировки.

Почему после разбора числа стали строками?

При ручном чтении через ЧтениеXML все значения приходят как текст. Приведение выполняется вручную: Число(), Дата() или через ОписаниеТипов.ПривестиЗначение(). Автоматическую типизацию даёт только фабрика XDTO с описанной схемой.

Что делать с очень большим XML-файлом?

Не загружайте его целиком в память. Читайте потоково через ЧтениеXML и обрабатывайте каждый логический блок (например, один документ) сразу после его закрытия, не накапливая всё дерево. При необходимости фиксируйте транзакции порциями.

Как обработать элементы CDATA?

Содержимое CDATA приходит как узел с типом ТипУзлаXML.Текст (или отдельный тип CDATA в зависимости от версии платформы) — обрабатывайте его так же, как обычный текст, значение доступно через Чтение.Значение.

Чем отличается Имя от ЛокальноеИмя у ЧтениеXML?

Имя содержит полное имя с префиксом пространства имён (например, ns:Товар), а ЛокальноеИмя — только часть без префикса (Товар). Сравнивайте узлы по ЛокальноеИмя, чтобы код не ломался при смене префикса в файле поставщика.

Итог

Что важно запомнить

Для произвольного XML используйте ЧтениеXML с рекурсивным разбором в Соответствие, одноимённые элементы собирайте в массив, атрибуты читайте сразу. При известной схеме применяйте фабрику XDTO. Перед записью в базу проверяйте типы, кодировку и количество элементов, а запись выполняйте в транзакции.