Чтение XML-файлов в 1С: потоковый разбор, XDTO и DOM

· 9 мин

Содержание

Для чтения 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.Текст Тогда
        Если ТекущийТег = "Наименование" Тогда
            Наименование = Чтение.Значение;
        ИначеЕсли ТекущийТег = "Цена" Тогда
            Цена = Число(Чтение.Значение);
        КонецЕсли;

    КонецЕсли;

КонецЦикла;

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

Ключевые моменты примера:

  1. 🔹 Атрибуты читаются на узле НачалоЭлемента — КоличествоАтрибутов() и пара ИмяАтрибута(Индекс) / ЗначениеАтрибута(Индекс), индекс с нуля. Можно короче: ЗначениеАтрибута("Код") по имени.
  2. 🔹 Текст элемента лежит не в открывающем теге, а в отдельном узле Текст, который идёт следующим шагом. Поэтому имя текущего тега запоминают в переменную.
  3. 🔹 ЛокальноеИмя — имя тега без префикса пространства имён; Имя вернёт имя вместе с префиксом (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: " + ОписаниеОшибки());
КонецПопытки;
Чтение.Закрыть();

Закрыть() выносят из Попытка — файл должен освободиться и при ошибке, иначе он останется заблокированным до перезапуска сеанса.

Типовые сбои и что с ними делать:

Диагностика: выберите симптом

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

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

Как прочитать XML не из файла, а из строки или двоичных данных?
Вместо ОткрытьФайл(Путь) вызовите УстановитьСтроку(Строка). Если XML пришёл двоичными данными, сначала получите из них строку в нужной кодировке, затем передайте её в УстановитьСтроку. Дальше разбор идёт так же.
Чем `ЛокальноеИмя` отличается от `Имя`?
ЛокальноеИмя — имя тега без префикса пространства имён (Товар). Имя возвращает имя вместе с префиксом (ns1:Товар). Для надёжного сравнения элементов берите ЛокальноеИмя плюс URIПространстваИмен, потому что префикс в разных файлах может отличаться.
Почему при чтении теряются или сдвигаются данные между тегами?
Чаще всего путают узлы: текст элемента лежит не в открывающем теге, а в отдельном узле типа Текст, который идёт следующим шагом цикла. Запоминайте имя текущего элемента в переменную и забирайте значение уже на узле Текст.
Как прочитать большой XML-файл без переполнения памяти?
Только потоково, через ЧтениеXML: он идёт по документу построчно и не держит его целиком в памяти. ФабрикаXDTO и ПостроительDOM загружают весь файл сразу и для файлов на десятки мегабайт не подходят.
Нужна ли XSD-схема, чтобы прочитать XML в 1С?
Нет. ЧтениеXML и ПостроительDOM схему не требуют — читают любой корректный XML. Схема нужна только для типизированного чтения через ФабрикаXDTO, где из неё берутся типы полей и правила проверки структуры.