Разбор XML в 1С 8.3: ЧтениеXML, DOM и XDTO на примерах кода

· 10 мин

Содержание

Разобрать XML в 1С можно тремя способами. Потоковое чтение объектом ЧтениеXML — когда файл большой и нужны только отдельные значения. Построение дерева ПостроительDOM — когда удобнее обращаться к узлам по имени и атрибутам. И СериализаторXDTO — когда XML описан схемой и его нужно превратить в типизированные объекты. Файл при любом подходе подаётся либо с диска через ОткрытьФайл, либо из текстовой переменной через УстановитьСтроку.

Выбор зависит от размера данных и задачи. На выгрузке в сотни мегабайт DOM упрётся в оперативную память, а ЧтениеXML пройдёт файл за один проход. На небольшом ответе веб-сервиса, наоборот, дерево читается нагляднее. Ниже — рабочие примеры для платформы 8.3 (интерфейс «Такси»), с кодом, который можно вставить в обработку и запустить.

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

Какие объекты языка понадобятся

За чтение XML в платформе отвечают несколько встроенных объектов, и путать их не нужно:

  • 📥 ЧтениеXML — потоковый (последовательный) разбор. Идёт по файлу сверху вниз, узел за узлом, и держит в памяти только текущую позицию.
  • 🌳 ПостроительDOM и ДокументDOM — загружают весь файл в дерево объектов, по которому можно перемещаться в любом направлении и искать элементы по имени.
  • 🧩 СериализаторXDTO и ФабрикаXDTO — разбор по схеме XSD: XML сразу превращается в объект с типизированными свойствами. Так читаются форматы обмена вроде EnterpriseData.
  • ✍️ ЗаписьXML — обратная операция, формирование XML. Для разбора не нужна, но часто идёт рядом.

Никакой внешней библиотеки или компоненты ставить не надо — все объекты входят в платформу. Для начала работы достаточно создать новую внешнюю обработку (Файл → Новый → Внешняя обработка) и писать в её модуле объекта.

Как загрузить XML-файл в 1С

У ЧтениеXML и ПостроительDOM источник задаётся одинаково. Два основных метода:

  1. ОткрытьФайл(ПутьКФайлу) — читает файл с диска. Путь указывается с точки зрения того компьютера, где выполняется код: на сервере это каталог сервера, а не рабочей станции пользователя.
  2. УстановитьСтроку(Текст) — разбирает XML, который уже лежит в строковой переменной. Удобно, когда ответ пришёл от HTTP-сервиса или хранится в реквизите.
// Из файла на сервере
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл("C:\Обмен\tovary.xml");

// Из строки (например, тело ответа веб-сервиса)
ЧтениеXMLСтр = Новый ЧтениеXML;
ЧтениеXMLСтр.УстановитьСтроку(ТекстОтвета);

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

Кодировка определяется из заголовка <?xml version="1.0" encoding="windows-1251"?> автоматически. Проблемы начинаются, когда в заголовке объявлена одна кодировка, а файл физически сохранён в другой — об этом в разделе про ошибки.

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

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

  • ТипУзла — что сейчас под курсором: начало элемента, конец элемента, текст (сравнивается со значениями перечисления ТипУзлаXML);
  • Имя и ЛокальноеИмя — имя тега (с префиксом пространства имён и без);
  • Значение — текстовое содержимое текстового узла;
  • ПолучитьАтрибут(ИмяАтрибута) — значение атрибута, когда курсор стоит на начале элемента.

Разберём простой файл товаров:

<Номенклатура>
    <Товар Код="00-0001">
        <Наименование>Карандаш</Наименование>
        <Цена>12.50</Цена>
    </Товар>
    <Товар Код="00-0002">
        <Наименование>Тетрадь</Наименование>
        <Цена>45.00</Цена>
    </Товар>
</Номенклатура>

Код, который вытащит код, наименование и цену каждого товара:

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

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

    Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента
        И ЧтениеXML.ЛокальноеИмя = "Товар" Тогда

        Код = ЧтениеXML.ПолучитьАтрибут("Код");
        Наименование = "";
        Цена = 0;

        // идём внутри текущего товара до его закрывающего тега
        Пока ЧтениеXML.Прочитать() Цикл

            Если ЧтениеXML.ТипУзла = ТипУзлаXML.КонецЭлемента
                И ЧтениеXML.ЛокальноеИмя = "Товар" Тогда
                Прервать;

            ИначеЕсли ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента
                И ЧтениеXML.ЛокальноеИмя = "Наименование" Тогда
                ЧтениеXML.Прочитать();
                Наименование = ЧтениеXML.Значение;

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

            КонецЕсли;

        КонецЦикла;

        Сообщить(СтрШаблон("%1 — %2, цена %3", Код, Наименование, Цена));

    КонецЕсли;

КонецЦикла;

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

Логика простая: внешний цикл ловит начало очередного товара, вложенный — собирает его поля и выходит на закрывающем теге. После чтения текстового узла значение лежит в свойстве Значение; строку в число переводим явно через Число().

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

Сравнивайте именно ЛокальноеИмя, а не Имя: если в файле появится префикс пространства имён (<n1:Товар>), свойство Имя вернёт n1:Товар и сравнение по "Товар" сломается.

Разбор через дерево DOM

Когда структура разветвлённая и к узлам удобнее обращаться по имени, стройте DOM. ПостроительDOM читает поток ЧтениеXML и возвращает объект ДокументDOM, по которому можно ходить в любом порядке.

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

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

Товары = Документ.ПолучитьЭлементыПоИмени("Товар");

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

Метод ПолучитьЭлементыПоИмени собирает все элементы с указанным именем на любой глубине. У каждого элемента доступны ПолучитьАтрибут(Имя) и ТекстовоеСодержимое — весь текст внутри узла и его потомков.

Для адресного поиска по сложному условию у ДокументаDOM есть механизм XPath. Он требует объекта-разыменователя пространств имён и возвращает набор узлов по выражению вида //Товар[@Код='00-0001']. Если пространств имён в файле нет и структура несложная, обычного перебора через ПолучитьЭлементыПоИмени обычно достаточно.

DOM держит весь документ в памяти целиком. На файлах в десятки и сотни мегабайт это даёт резкий рост потребления ОЗУ и может уронить рабочий процесс сервера. Для крупных выгрузок берите потоковое ЧтениеXML.

Когда брать XDTO вместо ручного разбора

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

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

Объект = СериализаторXDTO.ПрочитатьXML(ЧтениеXML);
ЧтениеXML.Закрыть();

Преимущество — типы приводятся автоматически: число станет числом, дата датой, вложенные объекты — объектами. Минус в том, что схема XSD должна быть известна фабрике типов. Для чужого, нестандартного XML без схемы это не работает — там остаются ЧтениеXML или DOM.

Выбрать подход помогает короткое дерево решений:

Каким способом разбирать ваш XML?

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

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

Практические правила:

  • 🎯 В ЧтениеXML сравнивайте теги по ЛокальноеИмя, а при необходимости дополнительно проверяйте URIПространстваИмен — так разбор не зависит от того, какой префикс выбрал отправитель.
  • 🔎 Атрибут из определённого пространства имён берётся расширенной формой ПолучитьАтрибут(ЛокальноеИмя, URIПространстваИмен).
  • 🌳 В XPath по DOM каждому префиксу нужно сопоставить его URI через разыменователь — иначе выражение не найдёт ни одного узла, хотя теги в файле есть.

Если один и тот же файл то читается, то нет в зависимости от выгрузки, первым делом смотрите, не добавился ли в корневом теге xmlns. Проверка по ЛокальноеИмя снимает большинство таких сюрпризов.

Обработка ошибок и отладка разбора

Любой разбор оборачивайте в Попытка … Исключение, потому что некорректный XML прерывает выполнение исключением:

Попытка
    ЧтениеXML = Новый ЧтениеXML;
    ЧтениеXML.ОткрытьФайл(ПутьКФайлу);
    // ... разбор ...
    ЧтениеXML.Закрыть();
Исключение
    ЗаписьЖурналаРегистрации("Разбор XML",
        УровеньЖурналаРегистрации.Ошибка, , ,
        ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;

Так сбой попадёт в журнал регистрации с текстом причины, а не оборвёт обмен молча. Для отладки удобно поставить точку останова в отладчике конфигуратора и в табло смотреть ТипУзла, ЛокальноеИмя и Значение на каждом шаге цикла — сразу видно, где позиция «съехала».

Самые частые ошибки и что с ними делать:

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

Как разобрать большой XML без нехватки памяти

Файлы обмена легко вырастают до сотен мегабайт, и здесь способ разбора решает всё.

  • Берите потоковое ЧтениеXML. Оно читает по одному узлу, поэтому расход памяти не зависит от размера файла.
  • Не собирайте результат в одну гигантскую таблицу значений в памяти. Записывайте данные порциями — например, по несколько тысяч строк в транзакции, затем очищайте накопитель.
  • Разбор запускайте на сервере, в фоновом задании. Клиентский сеанс жёстче ограничен по памяти и времени.
  • Уберите Сообщить() из тела цикла на больших объёмах — вывод в окно сообщений на сотнях тысяч итераций заметно тормозит обработку. Для контроля пишите только итог.

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

Пример: загрузка номенклатуры из XML в справочник

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

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

Счётчик = 0;
НачатьТранзакцию();

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

    Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента
        И ЧтениеXML.ЛокальноеИмя = "Товар" Тогда

        Код = ЧтениеXML.ПолучитьАтрибут("Код");
        Наименование = "";

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

        Элемент = Справочники.Номенклатура.СоздатьЭлемент();
        Элемент.Код = Код;
        Элемент.Наименование = Наименование;
        Элемент.Записать();

        Счётчик = Счётчик + 1;
        Если Счётчик % 1000 = 0 Тогда
            ЗафиксироватьТранзакцию();
            НачатьТранзакцию();
        КонецЕсли;

    КонецЕсли;

КонецЦикла;

ЗафиксироватьТранзакцию();
ЧтениеXML.Закрыть();

Транзакция на каждую тысячу записей ускоряет загрузку в разы по сравнению с записью по одному элементу. Для чужого Код из файла проверьте, нет ли уже такого элемента в справочнике, иначе получите дубли — поиск по коду добавляется перед СоздатьЭлемент.

Загрузку в рабочую базу запускайте после резервной копии: ошибка в соответствии полей способна создать сотни некорректных элементов, и откатывать их придётся вручную.

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

Чем разбор XML отличается от чтения JSON в 1С?
Логика похожа, но объекты другие: для JSON используются ЧтениеJSON и ПрочитатьJSON, которые сразу возвращают соответствие или массив. У XML структура богаче — есть атрибуты и пространства имён, поэтому и объектов разбора три (ЧтениеXML, DOM, XDTO).
Можно ли разобрать XML прямо на клиенте, без сервера?
Технически да, объекты доступны и на клиенте. Но на практике разбор выносят на сервер: там нет ограничений клиентского сеанса на память, доступен диск сервера и фоновые задания для долгих операций.
Как прочитать XML, который лежит в реквизите или в макете, а не в файле?
Получите его в строку и передайте в УстановитьСтроку(Текст) вместо ОткрытьФайл. Если данные в хранилище значения или в двоичных данных, сначала выгрузите их во временный файл или в строку нужной кодировки.
Почему при чтении числа и даты приходят как текст?
ЧтениеXML и DOM возвращают содержимое строками — типы нужно приводить вручную (Число(), Дата()). Автоматическое приведение типов даёт только XDTO, когда XML описан схемой XSD.
Файл в кодировке windows-1251 читается с «кракозябрами». Что делать?
Сверьте реальную кодировку файла с объявленной в заголовке <?xml ... encoding="..."?>. Чаще всего физическая кодировка не совпадает с заявленной либо в UTF-8 добавлен BOM — приведите их в соответствие в текстовом редакторе.