Разработка и Конфигурация · 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 без схемы, контроль памяти, но нужно писать обработчик узлов вручную
Подходит при известной схеме XSD; типы проверяются автоматически, но схему нужно создать или импортировать
Только для данных, ранее сериализованных самой платформой; для внешних файлов не работает
Чтение 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Откройте файл через ЧтениеXML.ОткрытьФайл() и убедитесь, что ошибок открытия нет
- 2Выполните первое Прочитать() и проверьте имя корневого элемента — оно должно совпадать с ожидаемым форматом
- 3Запустите рекурсивный разбор, собирая элементы в Соответствие, одноимённые — в Массив
- 4Проверьте на тестовом файле несколько известных значений: суммы, даты, количество строк
- 5Закройте ЧтениеXML и только после проверки данных запускайте запись в базу в транзакции
Проверка результата и контроль качества разбора
После разбора не спешите записывать данные в базу. Сначала убедитесь, что дерево соответствует ожиданиям: выведите в сообщение количество элементов верхнего уровня, несколько значений и типы данных. Простой способ — сериализовать полученное соответствие обратно через ЗаписатьJSON и посмотреть результат в отладчике.
Обращайте внимание на три типовые ситуации. Первая: элемент, который в одних файлах один, а в других повторяется, — код должен одинаково обрабатывать и единичное значение, и массив. Вторая: пустые элементы без текста — в дереве они будут пустыми соответствиями, и обращение к _Текст вернёт Неопределено. Третья: числа и даты при ручном разборе остаются строками — их нужно приводить явно с учётом регионального формата (десятичный разделитель, порядок дня и месяца).
Для приведения типов используйте ОписаниеТипов и метод ПривестиЗначение() или функции Число(), Дата() с обработкой исключений — строка вида «1 234,56» в Число() не преобразуется без предварительной очистки от пробелов и замены запятой.
Как проверить результат разбора
Совпадает с числом строк в исходном файле
Элементов меньше — одноимённые узлы затёрли друг друга
Числа приведены корректно, копейки не потеряны
Значения обрезаны или остались строками
Текст читается без иероглифов
Кракозябры — неверно определена кодировка файла
Все атрибуты попали в дерево
Атрибуты потеряны — читались после перехода к следующему узлу
Типичные ошибки и ограничения
Самая частая ошибка — потеря одноимённых элементов: если вставлять в структуру ключ без проверки существования, каждый следующий элемент с тем же именем перезапишет предыдущий, и из ста строк документа останется одна. Вторая по частоте проблема — кодировка: файл в windows-1251 без корректного объявления читается с искажением кириллицы. Кодировку можно задать явно при открытии: Чтение.ОткрытьФайл(Путь, , "windows-1251") варианта не существует, поэтому файл предварительно читают через ЧтениеТекста с нужной кодировкой и передают строку в УстановитьСтроку().
Третья группа ошибок связана с пространствами имён. Элементы с префиксами (ns:Документ) имеют локальное имя и URI пространства; сравнивать стоит Чтение.ЛокальноеИмя, а не полное Имя, иначе при смене префикса поставщиком код перестанет находить узлы. Также помните, что рекурсивный разбор очень глубоких документов теоретически ограничен стеком вызовов — для патологически вложенных файлов используйте итеративный вариант со стеком в массиве.
Практические рекомендации по внедрению
Оформляйте разбор как отдельную функцию в общем модуле с явным входом (путь к файлу или строка XML) и выходом (соответствие или типизированная структура). Это позволит покрыть функцию тестами на эталонных файлах и переиспользовать в разных обменах. Файлы-примеры от контрагентов сохраняйте в каталог тестовых данных — форматы внешних систем меняются без предупреждения.
Запись результатов в базу выполняйте в транзакции и только после полной валидации дерева. Для регламентных загрузок добавьте журналирование: имя файла, число прочитанных элементов, число отклонённых строк с причинами. Это сильно упрощает разбор расхождений, когда поставщик данных утверждает, что «всё отправил правильно».
Если формат файла контролируете вы сами (обмен между двумя своими базами 1С), не изобретайте ручной разбор: сериализуйте данные через ЗаписатьXML/ЗначениеВСтрокуВнутр и читайте штатными средствами — это устраняет целый класс ошибок с типами и кодировками.
Частые вопросы
Можно ли прочитать XML напрямую в Структуру без Соответствия?
Можно, если вы уверены, что все имена элементов — допустимые идентификаторы: латиница или кириллица, без пробелов, дефисов и двоеточий, не начинаются с цифры. Для внешних файлов с пространствами имён это условие обычно не выполняется, поэтому безопаснее Соответствие.
Как прочитать XML, полученный строкой из HTTP-ответа?
Создайте ЧтениеXML и вызовите УстановитьСтроку(ТекстОтвета) вместо ОткрытьФайл(). Дальнейший разбор идентичен файловому. Если ответ в кодировке, отличной от UTF-8, сначала получите тело через ПолучитьТелоКакСтроку() с указанием кодировки.
Почему после разбора числа стали строками?
При ручном чтении через ЧтениеXML все значения приходят как текст. Приведение выполняется вручную: Число(), Дата() или через ОписаниеТипов.ПривестиЗначение(). Автоматическую типизацию даёт только фабрика XDTO с описанной схемой.
Что делать с очень большим XML-файлом?
Не загружайте его целиком в память. Читайте потоково через ЧтениеXML и обрабатывайте каждый логический блок (например, один документ) сразу после его закрытия, не накапливая всё дерево. При необходимости фиксируйте транзакции порциями.
Как обработать элементы CDATA?
Содержимое CDATA приходит как узел с типом ТипУзлаXML.Текст (или отдельный тип CDATA в зависимости от версии платформы) — обрабатывайте его так же, как обычный текст, значение доступно через Чтение.Значение.
Чем отличается Имя от ЛокальноеИмя у ЧтениеXML?
Имя содержит полное имя с префиксом пространства имён (например, ns:Товар), а ЛокальноеИмя — только часть без префикса (Товар). Сравнивайте узлы по ЛокальноеИмя, чтобы код не ломался при смене префикса в файле поставщика.
Что важно запомнить
Для произвольного XML используйте ЧтениеXML с рекурсивным разбором в Соответствие, одноимённые элементы собирайте в массив, атрибуты читайте сразу. При известной схеме применяйте фабрику XDTO. Перед записью в базу проверяйте типы, кодировку и количество элементов, а запись выполняйте в транзакции.