Чтение XML-файлов в 1С: потоковый разбор, XDTO и DOM
Содержание
Для чтения XML в 1С 8.3 есть три штатных механизма: объект ЧтениеXML (потоковый, построчный разбор), ФабрикаXDTO (типизированное чтение по схеме) и ПостроительDOM (загрузка всего документа в дерево). Выбор зависит от размера файла и от того, есть ли под него XSD-схема.
Коротко: большой файл обмена построчно читают ЧтениеXML; документ с известной структурой и пространствами имён — через ФабрикаXDTO; небольшой файл, где нужен произвольный доступ к веткам, удобнее поднять в DOM. Ниже — рабочие примеры на встроенном языке, разбор пространств имён и перехват ошибок.
С чего начать: файл, кодировка и выбор механизма
Перед разбором убедитесь, что файл физически доступен коду. На сервере путь должен вести в каталог, куда у процесса 1cv8 есть доступ, — локальный диск клиента сервер не видит. Если XML приходит во временное хранилище или как двоичные данные, читать можно не из файла, а из строки.
Кодировку XML задаёт его пролог — строка вида <?xml version="1.0" encoding="UTF-8"?>. Если в прологе объявлена одна кодировка, а файл сохранён в другой, разбор упадёт на первом же нелатинском символе. Это самая частая причина ошибок чтения, поэтому кодировку проверяют до парсинга.
Способ открытия задаётся одним из методов:
- 📄
ОткрытьФайл(Путь)— чтение из файла на диске; - 🧵
УстановитьСтроку(Строка)— разбор XML, который уже лежит в переменной (пришёл из сервиса, из поля документа, изПолучитьСтроку()двоичных данных).
Как выбрать механизм:
- файл большой (десятки мегабайт, тысячи строк) — только потоковое
ЧтениеXML, память не раздувается; - под документ есть XSD-схема и описан XDTO-пакет —
ФабрикаXDTOдаёт сразу типизированный объект со свойствами; - файл небольшой, нужен произвольный доступ и поиск по тегам —
ПостроительDOM.
Потоковое чтение объектом ЧтениеXML
ЧтениеXML идёт по документу слева направо и на каждом шаге стоит на одном узле: открывающем теге, тексте, закрывающем теге, комментарии. Метод Прочитать() сдвигает позицию на следующий узел и возвращает Ложь, когда документ закончился. Тип текущего узла проверяют через ТипУзла и перечисление ТипУзлаXML.
Разбор накладной с товарными строками:
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл("C:\Обмен\nakladnaya.xml");
ТекущийТег = "";
Пока Чтение.Прочитать() Цикл
Если Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
ТекущийТег = Чтение.ЛокальноеИмя;
// атрибуты доступны прямо на открывающем теге
Для Инд = 0 По Чтение.КоличествоАтрибутов() - 1 Цикл
Если Чтение.ИмяАтрибута(Инд) = "Код" Тогда
КодТовара = Чтение.ЗначениеАтрибута(Инд);
КонецЕсли;
КонецЦикла;
ИначеЕсли Чтение.ТипУзла = ТипУзлаXML.Текст Тогда
Если ТекущийТег = "Наименование" Тогда
Наименование = Чтение.Значение;
ИначеЕсли ТекущийТег = "Цена" Тогда
Цена = Число(Чтение.Значение);
КонецЕсли;
КонецЕсли;
КонецЦикла;
Чтение.Закрыть();
Ключевые моменты примера:
- 🔹 Атрибуты читаются на узле
НачалоЭлемента—КоличествоАтрибутов()и параИмяАтрибута(Индекс)/ЗначениеАтрибута(Индекс), индекс с нуля. Можно короче:ЗначениеАтрибута("Код")по имени. - 🔹 Текст элемента лежит не в открывающем теге, а в отдельном узле
Текст, который идёт следующим шагом. Поэтому имя текущего тега запоминают в переменную. - 🔹
ЛокальноеИмя— имя тега без префикса пространства имён;Имявернёт имя вместе с префиксом (ns1:Товар).
Число(Чтение.Значение) сработает, если дробный разделитель в файле — точка. Если в данных запятая или разделители разрядов, разберите значение через СтрЗаменить или Формат перед преобразованием, иначе получите 0 или ошибку.
Когда в файле есть повторяющиеся блоки (строки накладной, позиции заказа), одной переменной под текущий тег мало — нужно понимать, внутри какой строки вы сейчас находитесь. Приём практика: ловить открытие и закрытие блока по имени, а поля собирать в структуру и сбрасывать её на закрывающем теге.
Товары = Новый Массив;
ТекСтрока = Неопределено;
Пока Чтение.Прочитать() Цикл
Если Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
Если Чтение.ЛокальноеИмя = "Товар" Тогда
ТекСтрока = Новый Структура("Наименование, Цена");
Иначе
ТекущийТег = Чтение.ЛокальноеИмя;
КонецЕсли;
ИначеЕсли Чтение.ТипУзла = ТипУзлаXML.Текст И ТекСтрока <> Неопределено Тогда
Если ТекущийТег = "Наименование" Тогда
ТекСтрока.Наименование = Чтение.Значение;
ИначеЕсли ТекущийТег = "Цена" Тогда
ТекСтрока.Цена = Число(Чтение.Значение);
КонецЕсли;
ИначеЕсли Чтение.ТипУзла = ТипУзлаXML.КонецЭлемента
И Чтение.ЛокальноеИмя = "Товар" Тогда
Товары.Добавить(ТекСтрока);
ТекСтрока = Неопределено;
КонецЕсли;
КонецЦикла;
Так каждая строка собирается независимо, а в Товары остаётся готовый массив структур. Тот же подход масштабируется на вложенность: держите не одну переменную, а стек открытых элементов.
Потоковое чтение не держит в памяти весь файл — поэтому им разбирают выгрузки на сотни тысяч строк. Платой за это идёт ручная логика: вы сами отслеживаете, внутри какого элемента находитесь.
Типизированное чтение через ФабрикуXDTO
Когда под XML есть схема (XSD) и описан пакет XDTO, разбирать вручную не нужно. Глобальная ФабрикаXDTO читает документ целиком и возвращает объект, у которого поля XML становятся свойствами.
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл("C:\Обмен\nakladnaya.xml");
Объект = ФабрикаXDTO.ПрочитатьXML(Чтение);
Чтение.Закрыть();
Номер = Объект.Номер;
Дата = Объект.Дата;
Для Каждого Строка Из Объект.Товары Цикл
Сообщить(СтрШаблон("%1 — %2 шт.", Строка.Наименование, Строка.Количество));
КонецЦикла;
Если корневой элемент соответствует типу, уже зарегистрированному в известной фабрике, тип можно не указывать. Для собственной схемы тип задают явно через пространство имён и имя:
ТипДокумента = ФабрикаXDTO.Тип("http://mycompany.ru/exchange", "Накладная");
Объект = ФабрикаXDTO.ПрочитатьXML(Чтение, ТипДокумента);
Что даёт XDTO по сравнению с ручным разбором:
- ✅ обращение к данным по именам свойств, а не по строковым именам тегов;
- ✅ автоматическое приведение типов из схемы (дата станет датой, число — числом);
- ✅ корректная работа с пространствами имён — фабрика сопоставляет элементы по URI, а не по префиксу.
Если по схеме поле необязательное, его может не быть в конкретном файле. Обращаться к нему напрямую рискованно — безопаснее проверить наличие свойства перед чтением и подставить значение по умолчанию, когда его нет. Иначе отсутствующее поле даст либо Неопределено, либо ошибку в зависимости от того, как описан тип.
ФабрикаXDTO.ПрочитатьXML держит весь документ в памяти. Для файлов обмена на десятки мегабайт это тяжело — такие читают потоково через ЧтениеXML.
Отдельно стоит СериализаторXDTO: его ПрочитатьXML(Чтение) восстанавливает значение платформы 1С (справочник, документ, структуру), ранее выгруженное в XML. Применяют при собственных механизмах обмена между базами 1С.
Загрузка в память: дерево DOM и поиск по тегам
ПостроительDOM строит из XML дерево ДокументDOM — после этого к любому элементу можно обратиться напрямую, без последовательного прохода. Удобно, когда структура нелинейная и одни и те же теги разбросаны по документу.
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл("C:\Обмен\nakladnaya.xml");
Построитель = Новый ПостроительDOM;
Документ = Построитель.Прочитать(Чтение);
Чтение.Закрыть();
УзлыТоваров = Документ.ПолучитьЭлементыПоИмени("Товар");
Для Каждого Узел Из УзлыТоваров Цикл
Наим = Узел.ПолучитьАтрибут("Наименование");
Сообщить(Наим);
КонецЦикла;
ПолучитьЭлементыПоИмени(Имя) возвращает список всех элементов с таким именем на любой глубине. У каждого узла есть ПолучитьАтрибут(Имя) для чтения атрибута и ТекстовоеСодержимое для текста внутри тега. От узла можно двигаться к потомкам и к родителю через ДочерниеУзлы и РодительскийУзел, то есть обходить дерево в любом направлении — чего потоковое чтение не умеет в принципе.
Минус очевиден: дерево DOM целиком лежит в оперативной памяти, поэтому для крупных файлов этот способ не годится. Его стихия — конфигурационные и небольшие обменные файлы, где важнее удобство навигации, чем экономия памяти.
Сравнение способов: что выбрать под задачу
Три механизма закрывают разные сценарии. Таблица — чтобы не гадать:
| Критерий | ЧтениеXML | ФабрикаXDTO | ПостроительDOM |
|---|---|---|---|
| Память | минимум, поток | весь документ | весь документ |
| Размер файла | любой, в т. ч. крупный | средний | небольшой |
| Нужна XSD-схема | нет | да (пакет XDTO) | нет |
| Доступ к данным | по тегам вручную | по свойствам объекта | по дереву и тегам |
| Произвольный доступ | нет, только вперёд | есть | есть |
| Типизация значений | ручная | автоматически по схеме | ручная |
Практический вывод:
- 🧵 поток данных, большой объём, схема неизвестна →
ЧтениеXML; - 📦 структурированный обмен, есть схема, нужны типы →
ФабрикаXDTO; - 🌳 небольшой файл, хаотичная структура, поиск по веткам →
ПостроительDOM.
Пространства имён: почему разбор ломается на чужих файлах
Пространства имён (namespaces) — частая причина, по которой «тот же код не читает соседний файл». В XML префикс (ns1:, d:) — это просто псевдоним длинного URI, объявленного через xmlns. Один и тот же документ можно записать с префиксом ns1, с префиксом d или вообще без префикса (пространство по умолчанию) — и это будет одно и то же пространство имён.
Отсюда правило: сравнивайте элементы по локальному имени и URI, а не по префиксу — префикс в каждом файле может быть свой.
В потоковом чтении для этого есть отдельные свойства узла:
ЛокальноеИмя— имя без префикса (Товар);URIПространстваИмен— полный URI пространства;Префикс— псевдоним, как он записан в файле;Имя— имя вместе с префиксом.
Если Чтение.ЛокальноеИмя = "Товар"
И Чтение.URIПространстваИмен = "http://mycompany.ru/exchange" Тогда
// это нужный элемент независимо от того, какой префикс в файле
КонецЕсли;
В XDTO об этом думать не нужно: тип в фабрике задаётся парой «URI + имя», и сопоставление идёт по ней. Поэтому для файлов с несколькими пространствами имён XDTO обычно надёжнее ручного разбора.
Обработка ошибок и проверка структуры
ЧтениеXML проверяет только правильность формы (well-formed): закрыты ли теги, корректна ли вложенность, совпадает ли кодировка. Соответствие схеме он не контролирует — это задача XDTO. Любой разбор оборачивают в Попытка, иначе битый файл обрушит процедуру с необработанным исключением.
Чтение = Новый ЧтениеXML;
Попытка
Чтение.ОткрытьФайл("C:\Обмен\nakladnaya.xml");
Пока Чтение.Прочитать() Цикл
// разбор
КонецЦикла;
Исключение
Сообщить("Не удалось прочитать XML: " + ОписаниеОшибки());
КонецПопытки;
Чтение.Закрыть();
Закрыть() выносят из Попытка — файл должен освободиться и при ошибке, иначе он останется заблокированным до перезапуска сеанса.
Типовые сбои и что с ними делать:
<?xml encoding="..."?> объявлена одна кодировка, а файл сохранён в другой. Пересохраните файл в заявленной кодировке или читайте из строки, предварительно перекодировав данные.&, <, >. В корректном XML они экранируются (&, <, >). Если файл формирует внешняя система — чинить нужно на её стороне.Чтобы валидировать содержимое по схеме, читают через ФабрикаXDTO с явным типом: несоответствие структуры сразу даст исключение. Для потокового чтения проверки добавляют вручную — например, считают, что все обязательные поля заполнены, уже после прохода по документу.
Частые вопросы
Как прочитать XML не из файла, а из строки или двоичных данных?
ОткрытьФайл(Путь) вызовите УстановитьСтроку(Строка). Если XML пришёл двоичными данными, сначала получите из них строку в нужной кодировке, затем передайте её в УстановитьСтроку. Дальше разбор идёт так же.Чем `ЛокальноеИмя` отличается от `Имя`?
ЛокальноеИмя — имя тега без префикса пространства имён (Товар). Имя возвращает имя вместе с префиксом (ns1:Товар). Для надёжного сравнения элементов берите ЛокальноеИмя плюс URIПространстваИмен, потому что префикс в разных файлах может отличаться.Почему при чтении теряются или сдвигаются данные между тегами?
Текст, который идёт следующим шагом цикла. Запоминайте имя текущего элемента в переменную и забирайте значение уже на узле Текст.Как прочитать большой XML-файл без переполнения памяти?
ЧтениеXML: он идёт по документу построчно и не держит его целиком в памяти. ФабрикаXDTO и ПостроительDOM загружают весь файл сразу и для файлов на десятки мегабайт не подходят.Нужна ли XSD-схема, чтобы прочитать XML в 1С?
ЧтениеXML и ПостроительDOM схему не требуют — читают любой корректный XML. Схема нужна только для типизированного чтения через ФабрикаXDTO, где из неё берутся типы полей и правила проверки структуры.