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

· 9 мин

Содержание

Чтобы прочитать XML в 1С, в большинстве случаев берут объект ЧтениеXML: он открывает файл или строку и выдаёт содержимое узел за узлом, не загружая всё в память. Для файлов с заранее известной структурой удобнее DOM (ПостроительDOM + ДокументDOM), а для обмена по схеме — типизированное чтение через ФабрикаXDTO.

Ниже — три рабочих подхода к разбору XML на встроенном языке, примеры кода, работа с атрибутами, пространствами имён и XSD-схемами, плюс разбор ошибок, на которые натыкаются чаще всего. Весь код работает на платформе 1С:Предприятие 8.3.

Три способа разобрать XML и когда какой брать

В платформе нет «одного правильного» объекта для XML — есть три инструмента под разные задачи. Выбор зависит от размера файла и от того, знаете ли вы его структуру заранее.

  • 📄 Потоковое чтение — ЧтениеXML. Читает файл последовательно, узел за узлом, в одном направлении. Память почти не расходует. Подходит для больших файлов и для случаев, когда структура плавает.
  • 🌳 DOM — ПостроительDOM и ДокументDOM. Загружает весь документ в память в виде дерева. Удобно обращаться к любому узлу, искать по XPath. Платёж за удобство — оперативная память.
  • 🧩 XDTO — ФабрикаXDTO. Разбирает XML в типизированные объекты по XSD-схеме. Нужен, когда есть формализованная схема обмена (ЕГАИС, ФНС, банковские выписки, обмен между конфигурациями).
СпособПамятьКогда брать
ЧтениеXMLминимумбольшие файлы, простой обход, экспорт логов
DOMвесь файлнебольшие файлы, выборка по XPath, правка узлов
XDTOвесь объектобмен по XSD-схеме, строгая типизация

Если файл весит десятки или сотни мегабайт — забудьте про DOM и XDTO-чтение целиком. Только потоковое чтение ЧтениемXML.

Потоковое чтение: разбираем файл узел за узлом

Базовый сценарий. Создаём объект, открываем источник, крутим цикл по узлам и смотрим тип текущего узла.

Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл("C:\Обмен\data.xml");

Пока Чтение.Прочитать() Цикл
    Если Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
        Сообщить("Открыт элемент: " + Чтение.Имя);
    ИначеЕсли Чтение.ТипУзла = ТипУзлаXML.Текст Тогда
        Сообщить("Текст: " + Чтение.Значение);
    ИначеЕсли Чтение.ТипУзла = ТипУзлаXML.КонецЭлемента Тогда
        Сообщить("Закрыт элемент: " + Чтение.Имя);
    КонецЕсли;
КонецЦикла;

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

Метод Прочитать() сдвигает курсор на следующий узел и возвращает Ложь, когда узлы закончились. На этом держится весь цикл.

Ключевые свойства текущего узла:

  • ТипУзла — что сейчас под курсором (НачалоЭлемента, КонецЭлемента, Текст, Комментарий и т. д.).
  • Имя — имя элемента или атрибута.
  • Значение — текстовое содержимое узла.

Если источник — не файл, а строка (например, пришла из HTTP-ответа), открывают её напрямую:

Чтение = Новый ЧтениеXML;
Чтение.УстановитьСтроку(ТекстОтвета);

Всегда закрывайте объект методом Закрыть(). Пока файл открыт ЧтениемXML, он занят процессом 1С, и повторная запись в него упадёт с ошибкой доступа.

Если целый элемент не нужен (например, пропускаете служебный блок), не крутите по нему цикл вручную — перешагните его одним вызовом ПропуститьЭлемент(). Курсор встанет сразу за закрывающим тегом, и дальше цикл пойдёт как обычно.

Разберём типовую задачу целиком: прочитать список товаров из файла и сложить их в таблицу значений. Структура файла — элементы <Товар> с атрибутами Код и Наименование и вложенным элементом <Цена>.

Товары = Новый ТаблицаЗначений;
Товары.Колонки.Добавить("Код");
Товары.Колонки.Добавить("Наименование");
Товары.Колонки.Добавить("Цена");

Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл("C:\Обмен\tovary.xml");

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

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

На выходе — заполненная ТаблицаЗначений, готовая к записи в регистр, документ или к сверке. Этот каркас (поймать начало нужного элемента → забрать атрибуты → поймать текст вложенного узла) закрывает большинство реальных обменов.

Как читать атрибуты и текст внутри элементов

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

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

Если известно имя конкретного атрибута, значение берут напрямую, не перебирая все:

Код = Чтение.ЗначениеАтрибута("Код");

Текст между открывающим и закрывающим тегами приходит отдельным узлом с типом ТипУзлаXML.Текст. Типовой приём — запомнить имя открытого элемента, на следующем шаге цикла забрать его текст:

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

Значения из XML приходят строками. Числа и даты нужно явно приводить (Число(), Дата()), а дату из ISO-формата 2026-10-07T12:00:00 — через XMLЗначение(Тип("Дата"), Чтение.Значение), иначе рискуете получить ошибку преобразования.

Чтение через DOM, когда нужен произвольный доступ к узлам

DOM загружает документ целиком и даёт дерево, по которому можно ходить в любую сторону и делать выборки. Строят дерево из того же ЧтенияXML:

ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл("C:\Обмен\data.xml");

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

Дальше обращаются к элементам по имени:

Элементы = ДокументDOM.ПолучитьЭлементыПоИмени("Товар");
Для Каждого Элемент Из Элементы Цикл
    Сообщить(Элемент.ТекстовоеСодержимое);
КонецЦикла;

Для сложной выборки удобнее XPath — он вычисляется методом ВычислитьВыражениеXPath. Если в документе есть пространства имён, XPath без разыменователя работать не будет (об этом ниже).

Главное ограничение DOM — память. Дерево держит в оперативке весь файл плюс накладные расходы на объекты узлов. Для файла в несколько мегабайт это нормально, для сотен мегабайт — путь к Недостаточно памяти.

Типизированное чтение по XSD-схеме через XDTO

Когда у обмена есть XSD-схема, разбирать XML вручную не нужно — платформа сама соберёт типизированные объекты. Сначала загружают схему и строят по ней фабрику:

ЧтениеСхемы = Новый ЧтениеXML;
ЧтениеСхемы.ОткрытьФайл("C:\Обмен\scheme.xsd");
НаборСхем = ФабрикаXDTO.ПрочитатьНаборСхемXML(ЧтениеСхемы);
ЧтениеСхемы.Закрыть();

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

Затем читают данные этой фабрикой:

ЧтениеДанных = Новый ЧтениеXML;
ЧтениеДанных.ОткрытьФайл("C:\Обмен\data.xml");
Объект = Фабрика.ПрочитатьXML(ЧтениеДанных);
ЧтениеДанных.Закрыть();

Сообщить(Объект.Номер);
Сообщить(Объект.Дата);

К свойствам полученного объекта обращаются по именам из схемы — платформа уже привела типы (число останется числом, дата — датой).

Если XML — это сериализованный прикладной объект 1С (документ, элемент справочника, выгруженный ЗаписатьXML), его читают глобальной фабрикой и СериализаторомXDTO:

ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(Путь);
Если ВозможностьЧтенияXML(ЧтениеXML) Тогда
    Значение = СериализаторXDTO.ПрочитатьXML(ЧтениеXML);
КонецЕсли;
ЧтениеXML.Закрыть();

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

Пространства имён: почему элементы «не находятся»

Самая частая засада при чтении обменных файлов — пространства имён (namespaces). Элемент в файле выглядит как <ns:Товар> или объявлен через xmlns="http://...", и поиск по короткому имени "Товар" ничего не возвращает.

При потоковом чтении имя делится на две части:

  • Чтение.Имя — имя с префиксом, как в файле (ns:Товар).
  • Чтение.ЛокальноеИмя — имя без префикса (Товар).
  • Чтение.URIПространстваИмен — URI пространства, к которому относится узел.

Сравнивайте элементы по ЛокальноеИмя и по URI пространства, а не по полному имени с префиксом — префикс в файле может быть любым, а URI постоянен.

Для XPath в DOM нужен разыменователь пространств имён — он сопоставляет префиксы из вашего выражения с URI документа:

Разыменователь = ДокументDOM.СоздатьРазыменовательПространствИмен();
Результат = ДокументDOM.ВычислитьВыражениеXPath(
    "//d:Товар", ДокументDOM, Разыменователь);

Без разыменователя XPath по файлу с namespace вернёт пустоту, хотя данные в файле есть.

Частые ошибки при чтении XML и как их обойти

Разбор чужого XML почти всегда упирается в данные, а не в код. Вот типовые симптомы и причины.

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

Чтобы не ловить незакрытый файл при исключении, оборачивайте чтение в Попытка:

Чтение = Новый ЧтениеXML;
Попытка
    Чтение.ОткрытьФайл(Путь);
    // разбор
Исключение
    Сообщить(ОписаниеОшибки());
КонецПопытки;
Чтение.Закрыть();

Оптимизация: как читать большие XML без тормозов

Разница между подходами на большом файле — не в скорости, а в том, запустится код вообще или упадёт по памяти. Несколько правил, которые держат разбор лёгким.

  • ⚡ Только потоковое чтение. Для файлов от нескольких мегабайт — ЧтениеXML и обход в цикле. DOM и чтение всего объекта XDTO исключаем.
  • 🧹 Не копите данные в памяти. Обработали узел (записали в таблицу значений, в регистр, в файл) — не держите его. Иначе смысл потокового чтения теряется.
  • 📦 Пишите пачками. Если по ходу разбора пишете в базу, накапливайте строки и проводите транзакцией по N записей, а не по одной.
  • 🧵 Читайте из потока. Если файл приходит из архива или по сети, открывайте ЧтениемXML поток напрямую — не выгружайте весь файл на диск ради чтения.
  • 🚫 Не логируйте каждый узел. Сообщить() в цикле на миллионах узлов сам по себе тормозит сильнее разбора. Логируйте итог или каждую N-ю запись.

Полезный приём для огромных однотипных файлов — «перемотка»: доходите потоковым чтением до нужного крупного элемента, а уже его небольшое поддерево при необходимости читаете через ПрочитатьФрагмент или собираете локальный DOM из этого куска.

Проверьте, что ваш разбор готов к боевому файлу:

Чек-лист перед запуском разбора XML
Выполнено: 0 / 6

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

Чем `ЧтениеXML` отличается от `ПостроителяDOM`?
ЧтениеXML читает файл потоком, узел за узлом, и почти не тратит память — но ходить можно только вперёд. ПостроительDOM загружает весь документ в память в виде дерева: доступ к любому узлу и XPath, но за счёт оперативки. Поток — для больших файлов, DOM — для небольших с произвольной выборкой.
Как прочитать XML из строки, а не из файла?
Создайте ЧтениеXML и вызовите УстановитьСтроку(ТекстXML) вместо ОткрытьФайл. Дальше разбор идёт тем же циклом Прочитать(). Удобно для XML, пришедшего в HTTP-ответе.
Почему элемент не находится, хотя в файле он есть?
Почти наверняка в файле есть пространства имён. Ищите элемент по ЛокальноеИмя и сверяйте URIПространстваИмен, а не по полному имени с префиксом. Для XPath задайте разыменователь пространств имён, иначе выборка вернёт пусто.
Как привести значение из XML к числу или дате?
Текст узла берётся из Значение и всегда приходит строкой. Число — через Число(), дату в ISO-формате — через XMLЗначение(Тип("Дата"), Чтение.Значение). Прямое Дата() на строке вида 2026-10-07T00:00:00 сработает не всегда.
Нужна ли XSD-схема, чтобы прочитать XML?
Нет. Потоковое чтение и DOM работают без схемы — вы сами разбираете узлы. Схема нужна только для XDTO-чтения, когда хотите сразу получить типизированные объекты с приведёнными типами полей.
Как не словить «нет доступа к файлу» после чтения?
Закрывайте объект методом Закрыть() сразу после разбора и делайте это даже при ошибке — внутри Попытка … Исключение. Пока файл открыт ЧтениемXML, он занят процессом, и повторный доступ к нему упадёт.