Разбор XML-файла во встроенном языке 1С 8.3: от тегов до таблицы значений

· 10 мин

Содержание

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

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

Чем читать XML в 1С: три объекта и когда какой брать

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

Встроенный язык 1С даёт три уровня работы с таким файлом:

ОбъектКак читаетПамятьКогда брать
ЧтениеXMLПоследовательно, узел за узломМинимум, не зависит от размера файлаБольшие файлы, простая плоская структура
ПостроительDOM → ДокументDOMСтроит всё дерево в памятиВ разы больше размера файлаСложная вложенность, выборки через XPath
ФабрикаXDTOПревращает XML в объектыКак у DOMЕсть XSD-схема или XDTO-пакет, нужен доступ по именам свойств

Есть и четвёртый путь — глобальная функция ПрочитатьXML(). Она восстанавливает значения, которые 1С сама записала через ЗаписатьXML(): справочники, документы, таблицы значений. Для чужих файлов она не годится.

Способы комбинируются. Частый приём практиков: идти по большому файлу через ЧтениеXML, а каждый повторяющийся блок отдавать в ФабрикаXDTO. Так память не растёт, а код остаётся коротким. Пример — в разделе про XDTO.

Как открыть XML-файл в 1С: с диска, из строки, с клиента на сервер

У ЧтениеXML два источника данных:

  • 📄 ОткрытьФайл(ИмяФайла) — файл на диске того компьютера, где выполняется код;
  • 🧵 УстановитьСтроку(Текст) — XML уже лежит в строковой переменной (пришёл из HTTP-запроса, из реквизита, из письма).

Главная ловушка — где лежит файл. Пользователь выбирает его на своём компьютере, а код разбора работает на сервере 1С. Путь C:\Users\buh\Desktop\upd.xml на сервере не существует. Файл нужно передать.

Передача файла с клиента на сервер

  1. На клиенте вызовите НачатьПомещениеФайлаНаСервер() или НачатьПомещениеФайлов() — файл попадёт во временное хранилище, вы получите адрес.
  2. Передайте адрес в серверную процедуру.
  3. На сервере получите двоичные данные и запишите их во временный файл:
ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресФайла);
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xml");
ДвоичныеДанные.Записать(ИмяВременногоФайла);

Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл(ИмяВременногоФайла);
// ... разбор ...
Чтение.Закрыть();
УдалитьФайлы(ИмяВременногоФайла);

Закрыть() обязателен до удаления. Иначе файл останется занятым, а на Windows-сервере УдалитьФайлы упадёт с ошибкой доступа.

Кодировка

Кодировку ЧтениеXML берёт из первой строки файла: <?xml version="1.0" encoding="windows-1251"?>. Если объявления нет, файл читается как UTF-8. Выгрузки старых программ иногда приходят в windows-1251 без объявления — вместо кириллицы получаются «кракозябры» или ошибка разбора.

Решение — прочитать текст с явной кодировкой и подать строкой:

ЧтениеТекста = Новый ЧтениеТекста(ИмяФайла, КодировкаТекста.ANSI);
ТекстXML = ЧтениеТекста.Прочитать();
ЧтениеТекста.Закрыть();

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

КодировкаТекста.ANSI на русской Windows — это windows-1251. На Linux-сервере надёжнее указать строку "windows-1251" вторым параметром.

Потоковое чтение XML в 1С: разбор от тегов к данным

Возьмём прайс поставщика:

<?xml version="1.0" encoding="UTF-8"?>
<Items>
  <Item code="001">
    <Name>Бумага А4, 500 л.</Name>
    <Price>350.00</Price>
  </Item>
  <Item code="002">
    <Name>Скрепки 28 мм</Name>
    <Price>42.50</Price>
  </Item>
</Items>

ЧтениеXML видит его как ленту узлов. Каждый вызов Прочитать() сдвигает курсор на один узел и возвращает Ложь, когда файл закончился. Тип текущего узла — в свойстве ТипУзла:

  • 🟢 ТипУзлаXML.НачалоЭлемента — открывающий тег <Item code="001">, здесь же доступны атрибуты;
  • 🔤 ТипУзлаXML.Текст — содержимое между тегами, читается из свойства Значение;
  • 🔴 ТипУзлаXML.КонецЭлемента — закрывающий тег </Item>.

Код функции общего серверного модуля, которая складывает прайс в таблицу значений:

Функция ПрочитатьПрайс(ИмяФайла) Экспорт

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

    Чтение = Новый ЧтениеXML;
    Чтение.ОткрытьФайл(ИмяФайла);

    ТекущаяСтрока = Неопределено;
    ТекущееПоле = "";

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

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

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

        ИначеЕсли Чтение.ТипУзла = ТипУзлаXML.КонецЭлемента Тогда
            ТекущееПоле = "";
        КонецЕсли;

    КонецЦикла;

    Чтение.Закрыть();
    Возврат Таблица;

КонецФункции

На выходе — две строки: 001 | Бумага А4, 500 л. | 350 и 002 | Скрепки 28 мм | 42,5.

Три детали, на которых спотыкаются новички:

  1. XMLЗначение вместо Число(). В XML дробная часть отделяется точкой, а Число("42.50") зависит от региональных настроек. XMLЗначение(Тип("Число"), ...) всегда понимает точку. Для дат то же самое: XMLЗначение(Тип("Дата"), "2026-10-07T00:00:00").
  2. ЛокальноеИмя, а не Имя. В Имя попадает префикс: ns2:Item. Сравнение с "Item" молча не сработает.
  3. Сброс поля на закрывающем теге. Без него пробелы и переводы строк между тегами запишутся в последнее поле.

Если нужен не весь файл, а один блок, ненужные ветки пропускают методом Пропустить(): курсор перепрыгнет за закрывающий тег текущего элемента.

Пространства имён, атрибуты и вложенность: DOM и XPath

Реальные файлы сложнее прайса. У УПД, ответов веб-сервисов и выгрузок ЕГАИС есть пространства имён: xmlns="urn:example:invoice" или префиксы вида ns2:. Элементы с одинаковым именем встречаются на разных уровнях. Писать для этого конечный автомат на ЧтениеXML долго — удобнее DOM.

Построение дерева

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

После Прочитать() всё дерево уже в памяти, файл можно закрыть. Простой поиск — Документ.ПолучитьЭлементыПоИмени("Item"). Если в файле есть пространство имён, используйте вариант с двумя параметрами: URI и локальное имя.

Выборка через XPath с пространством имён

Типичная ошибка: XPath //Item возвращает пусто, хотя элементы в файле есть. Причина — пространство имён по умолчанию. Для XPath элемент <Item> внутри xmlns="urn:example:invoice" — это не просто Item. Нужен префикс и разыменователь:

Разыменователь = Новый РазыменовательПространствИменDOM("d", "urn:example:invoice");

Результат = Документ.ВычислитьВыражениеXPath("//d:Item", Документ,
    Разыменователь, ТипРезультатаDOMXPath.УпорядоченныйИтераторУзлов);

Узел = Результат.ПолучитьСледующий();
Пока Узел <> Неопределено Цикл
    Код = Узел.ПолучитьАтрибут("code");
    Наименование = Документ.ВычислитьВыражениеXPath("d:Name", Узел,
        Разыменователь, ТипРезультатаDOMXPath.Строка).СтроковоеЗначение;
    Сообщить(Код + " — " + Наименование);
    Узел = Результат.ПолучитьСледующий();
КонецЦикла;

Префикс d придуман нами, в файле его нет — важно только совпадение URI. Второй вызов XPath ищет Name относительно текущего узла Item, поэтому путь без //.

Атрибуты без префикса в XML не принадлежат пространству имён по умолчанию. ПолучитьАтрибут("code") работает без разыменователя, а @d:code в XPath ничего не найдёт.

Чтение XML через XDTO и перенос в таблицу значений

ФабрикаXDTO.ПрочитатьXML() превращает XML в объект, у которого дочерние элементы и атрибуты становятся свойствами. Обращение Данные.Item выглядит проще любого цикла по узлам.

Без схемы

Чтение = Новый ЧтениеXML;
Чтение.УстановитьСтроку(ТекстXML);
Данные = ФабрикаXDTO.ПрочитатьXML(Чтение);
Чтение.Закрыть();

Если Данные.Установлен("Item") Тогда
    Элементы = Данные.Item;
    Если ТипЗнч(Элементы) <> Тип("СписокXDTO") Тогда
        Элементы = ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве(Элементы);
    КонецЕсли;
    Для Каждого Элемент Из Элементы Цикл
        Строка = Таблица.Добавить();
        Строка.Код = Элемент.code;
        Строка.Наименование = Элемент.Name;
        Строка.Цена = XMLЗначение(Тип("Число"), Элемент.Price);
    КонецЦикла;
КонецЕсли;

Без схемы у XDTO есть подвох: если элемент Item в файле один, свойство вернёт сам объект. Если их несколько — СписокXDTO. Код, проверенный на файле с одной строкой, сломается на файле с двумя. Проверка типа выше закрывает этот случай. ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве есть в конфигурациях на БСП; в своей базе без БСП создайте массив и добавьте элемент вручную.

Со схемой

Если поставщик дал XSD, импортируйте её в конфигурацию как XDTO-пакет. Тогда типы известны заранее, а чтение указывает тип явно:

ТипДокумента = ФабрикаXDTO.Тип("urn:example:invoice", "Items");
Данные = ФабрикаXDTO.ПрочитатьXML(Чтение, ТипДокумента);
Данные.Проверить();

Числа и даты приходят уже нужного типа, повторяющиеся элементы всегда приходят списком, а Проверить() вызывает исключение, если файл не соответствует схеме.

Большой файл по кусочкам

Совмещаем потоковое чтение и XDTO. Курсор ЧтениеXML доходит до <Item>, фабрика читает только этот блок и сдвигает курсор дальше:

Пока Чтение.Прочитать() Цикл
    Пока Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента
        И Чтение.ЛокальноеИмя = "Item" Цикл
        Элемент = ФабрикаXDTO.ПрочитатьXML(Чтение);
        // обработка одного Элемента
    КонецЦикла;
КонецЦикла;

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

Автоматическая загрузка XML: обработка, фоновое и регламентное задание

Для пользователя разбор оформляют внешней обработкой или формой в расширении: поле выбора файла, кнопка «Загрузить», таблица с результатом для проверки перед записью.

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

Параметры = Новый Массив;
Параметры.Добавить(АдресФайла);
ФоновыеЗадания.Выполнить("ЗагрузкаXML.ЗагрузитьПрайс", Параметры,
    , "Загрузка прайса из XML");

Процедура ЗагрузитьПрайс должна лежать в общем серверном модуле и быть экспортной. В типовых конфигурациях на БСП вместо прямого вызова удобнее ДлительныеОперации.ВыполнитьВФоне — он сам показывает индикатор ожидания и возвращает результат в форму.

Когда поставщик кладёт файлы в общую папку каждое утро, подключают регламентное задание: оно смотрит каталог через НайтиФайлы(Каталог, "*.xml"), разбирает каждый файл и переносит обработанные в подпапку. На файловой базе регламентные задания выполняются только при запущенном клиенте, поэтому для такой схемы нужен клиент-серверный вариант.

Перед запуском автоматической загрузки XML
Выполнено: 0 / 5

Скорость и безопасность при работе с XML

Ориентиры по скорости:

  • 🚀 Выбор объекта. DOM для файла на 200 МБ может занять гигабайты памяти рабочего процесса. Порог, после которого стоит переходить на ЧтениеXML, — примерно десятки мегабайт.
  • 🔁 Запросы вне цикла. Самое медленное в загрузке — не разбор, а поиск номенклатуры по коду для каждой строки. Сначала прочитайте XML в таблицу значений, затем одним запросом найдите все ссылки через временную таблицу.
  • 🧹 Не держите лишнее. Не копите миллион строк в памяти, если их можно записывать порциями по 1000–5000 с отдельной транзакцией на порцию.

Безопасность чужого файла:

  • 🛡️ Проверяйте размер до разбора. Файл на 2 ГБ, загруженный «по ошибке», остановит рабочий процесс при чтении через DOM.
  • ✅ Проверяйте структуру: схема и Проверить() у XDTO или явная проверка обязательных полей перед записью.
  • 🗂️ Не стройте пути и имена файлов из содержимого XML — значение тега может содержать ..\.

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

Ошибки при чтении XML в 1С и как их исправить

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

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

Можно ли просто посмотреть XML-файл без программирования?
Да. Любой XML открывается в браузере или текстовом редакторе с подсветкой. Для загрузки данных в типовую конфигурацию обычно хватает встроенных команд загрузки электронных документов или обработки универсального обмена — код нужен для нестандартных форматов.
Чем ЧтениеXML отличается от ПрочитатьXML?
ЧтениеXML — объект для разбора любого XML. Глобальная функция ПрочитатьXML() восстанавливает объекты 1С из XML, который записала сама платформа через ЗаписатьXML(). Перед её вызовом можно проверить файл функцией ВозможностьЧтенияXML().
Как прочитать XML, пришедший ответом от веб-сервиса?
Тело ответа получите строкой через ПолучитьТелоКакСтроку() у HTTP-ответа и передайте в ЧтениеXML.УстановитьСтроку(). Файл на диск записывать не нужно.
Как получить все атрибуты элемента, если их имена неизвестны?
На узле НачалоЭлемента вызывайте ЧтениеXML.ПрочитатьАтрибут() в цикле, пока он возвращает Истина. На каждом шаге имя атрибута — в ЛокальноеИмя, значение — в Значение.
Работает ли чтение XML на клиенте?
Часть объектов для XML доступна и в клиентском контексте, но набор отличается от серверного, а файл на клиенте в веб-клиенте недоступен напрямую. Надёжная схема — передать файл на сервер и разбирать там.