Чтение XML в 1С:Предприятии 8.3 — от готовой обработки до XDTO

· 10 мин

Содержание

Прочитать XML-файл в 1С 8.3 можно четырьмя способами. Если файл выгружен из такой же базы, его загружают готовой обработкой «Выгрузка и загрузка данных XML». Если файл чужой (прайс поставщика, ответ сервиса, выгрузка с сайта), его читают кодом: потоково через ЧтениеXML, деревом через ПостроительDOM или объектами через ФабрикаXDTO.

Типичная ситуация: поставщик раз в неделю присылает прайс в XML, и бухгалтер вручную переносит цены в 1С. Процедура на 40 строк кода делает то же за секунды. Ниже разобраны все варианты чтения — с кодом, который можно взять за основу, и с ловушками, на которых обычно спотыкаются.

Какой способ чтения XML выбрать

Способ зависит от двух вещей: откуда файл и насколько он большой. Сравнение в одной таблице:

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

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

Какой у вас файл?

Загрузка XML готовой обработкой без программирования

Для переноса справочников и документов между двумя базами одной конфигурации используют универсальную внешнюю обработку «Выгрузка и загрузка данных XML». Для платформы 8.3 это файл ВыгрузкаЗагрузкаДанныхXML83.epf. Его распространяют с дисками ИТС и в комплекте поставки обработок для разработчиков.

Порядок работы:

  1. В базе-приёмнике откройте обработку через Файл → Открыть. Нужны права на открытие внешних обработок; в типовых конфигурациях при первом открытии появится предупреждение защиты от опасных действий.
  2. Перейдите на вкладку загрузки и укажите XML-файл, выгруженный этой же обработкой из базы-источника.
  3. Запустите загрузку и дождитесь сообщения об окончании.

Что важно знать до запуска:

  • 🔑 Объекты загружаются со своими уникальными идентификаторами. Если в приёмнике уже есть элемент с тем же идентификатором, он будет перезаписан без вопросов.
  • 🧩 Имена объектов метаданных в источнике и приёмнике должны совпадать. Выгрузку из Бухгалтерии в ЗУП так не перенести.
  • 🚫 Запись идёт в режиме обмена данными: проверки заполнения и часть подписок на события не срабатывают. Документы после загрузки обычно нужно перепровести.

Перед загрузкой сделайте резервную копию базы-приёмника. Откатить перезаписанные элементы обработка не умеет.

Для обмена между разными конфигурациями служит обработка «Универсальный обмен данными в формате XML». Она читает файл по правилам конвертации, которые готовят в конфигурации «Конвертация данных». Без правил она бесполезна.

Чужой XML (прайс, заказ из интернет-магазина, ответ веб-сервиса) ни одна из этих обработок не прочитает. Для него нужен код — дальше разбираем, как его написать.

Как передать XML-файл с компьютера пользователя на сервер

Код разбора лучше выполнять на сервере: там нет ограничений тонкого и веб-клиента, а результат сразу можно записать в базу. Файл же лежит на компьютере пользователя. Поэтому сначала его помещают во временное хранилище:

&НаКлиенте
Процедура ЗагрузитьXML(Команда)
    Оповещение = Новый ОписаниеОповещения("ПослеПомещенияФайла", ЭтотОбъект);
    НачатьПомещениеФайла(Оповещение, , , Истина, УникальныйИдентификатор);
КонецПроцедуры

&НаКлиенте
Процедура ПослеПомещенияФайла(Результат, Адрес, ВыбранноеИмяФайла, ДополнительныеПараметры) Экспорт
    Если Результат Тогда
        ЗагрузитьНаСервере(Адрес);
    КонецЕсли;
КонецПроцедуры

&НаСервере
Процедура ЗагрузитьНаСервере(Адрес)
    ДвоичныеДанные = ПолучитьИзВременногоХранилища(Адрес);
    ИмяФайла = ПолучитьИмяВременногоФайла("xml");
    ДвоичныеДанные.Записать(ИмяФайла);
    
    Товары = ПрочитатьТовары(ИмяФайла);
    // ... запись данных в базу
    
    УдалитьФайлы(ИмяФайла);
КонецПроцедуры

💡 Асинхронный вызов НачатьПомещениеФайла работает и в тонком клиенте, и в веб-клиенте. Модальные диалоги в современных конфигурациях часто запрещены свойством «Режим использования модальности», так что синхронные варианты лучше не применять.

Если файл уже лежит на сервере (например, его кладёт туда внешний сервис), шаг с хранилищем не нужен: путь сразу передают в ЧтениеXML.ОткрытьФайл(). Путь должен быть доступен пользователю, от имени которого работает сервер 1С, а не вашей учётной записи.

Потоковое чтение XML через объект ЧтениеXML

ЧтениеXML идёт по файлу узел за узлом. В каждый момент в памяти находится только текущий узел, поэтому так читаются и файлы на сотни мегабайт.

Возьмём простой прайс:

<?xml version="1.0" encoding="UTF-8"?>
<Товары>
  <Товар Код="001">
    <Наименование>Бумага А4</Наименование>
    <Цена>350.00</Цена>
  </Товар>
  <Товар Код="002">
    <Наименование>Папка-регистратор</Наименование>
    <Цена>189.50</Цена>
  </Товар>
</Товары>

Функция разбирает его в таблицу значений:

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

Как это работает:

  1. Прочитать() переходит к следующему узлу и возвращает Ложь, когда файл закончился.
  2. На открывающем теге <Товар> создаётся строка таблицы, атрибут Код берётся через ПолучитьАтрибут().
  3. Текст внутри <Наименование> и <Цена> приходит отдельным узлом типа Текст — сразу после начала элемента.
  4. На закрывающем </Товар> строка «отпускается», чтобы случайный текст вне товара не попал в таблицу.

Пара приёмов, без которых код быстро ломается:

  • 🏷️ ЛокальноеИмя вместо Имя. Если в файле есть префиксы пространств имён (<ns1:Товар>), свойство Имя вернёт ns1:Товар, и сравнение не сработает.
  • 🔢 XMLЗначение() для чисел и дат. XML хранит число с точкой (350.00), дату — как 2025-03-14T00:00:00. Функция XMLЗначение(Тип("Число"), ...) и XMLЗначение(Тип("Дата"), ...) преобразует их корректно, а Число("350.00") на сервере с русскими региональными настройками может упасть.
  • 📄 Чтение из строки. Если XML пришёл ответом HTTP-сервиса, файл не нужен: Чтение.УстановитьСтроку(ТекстОтвета).

Разбор XML деревом: ПостроительDOM и XPath

Когда структура глубокая (документ → шапка → контрагент → адрес → индекс), отслеживать вложенность в потоковом цикле неудобно. Проще загрузить файл в дерево DOM и брать нужные узлы по имени или по пути:

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

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

Для выборки по условию подходит XPath. Например, все товары дороже 200:

Разыменователь = Документ.СоздатьРазыменовательПИ();
Результат = Документ.ВычислитьВыражениеXPath("//Товар[Цена > 200]", Документ, Разыменователь);
Узел = Результат.ПолучитьСледующий();
Пока Узел <> Неопределено Цикл
    Сообщить(Узел.ПолучитьАтрибут("Код"));
    Узел = Результат.ПолучитьСледующий();
КонецЦикла;

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

💡 Если ПолучитьЭлементыПоИмени ничего не находит, а элементы в файле точно есть — скорее всего, у документа задано пространство имён по умолчанию (xmlns="..."). Тогда используйте вариант метода с URI пространства имён или выражение XPath с префиксом, который знает разыменователь.

Чтение XML через XDTO: объекты вместо тегов

XDTO превращает XML в объекты: к элементу обращаются как к свойству, без циклов по узлам. Есть два режима.

Без схемы (нетипизированное чтение)

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

Список = Данные.Товар;
Если ТипЗнч(Список) = Тип("ОбъектXDTO") Тогда
    // в файле один товар — оборачиваем, чтобы цикл работал одинаково
    Массив = Новый Массив;
    Массив.Добавить(Список);
    Список = Массив;
КонецЕсли;

Для Каждого Товар Из Список Цикл
    Код = Товар.Код;
    Наименование = Товар.Наименование;
    Цена = XMLЗначение(Тип("Число"), Товар.Цена);
КонецЦикла;

ПрочитатьXML возвращает корневой элемент, атрибуты и вложенные элементы доступны как свойства. Главная ловушка этого режима видна в коде: если товар в файле один, свойство вернёт ОбъектXDTO, если несколько — СписокXDTO. Код, написанный на тестовом файле с десятью товарами, падает на первом реальном файле с одним. Значения без схемы приходят строками, поэтому числа и даты всё равно преобразуют через XMLЗначение().

По схеме XSD (типизированное чтение)

Если у формата есть XSD-схема, её загружают в конфигурацию как XDTO-пакет:

  1. В Конфигураторе откройте ветку Общие → XDTO-пакеты.
  2. Вызовите контекстное меню и импортируйте XML-схему из файла .xsd.
  3. Посмотрите в свойствах пакета его пространство имён (URI) — оно понадобится в коде.
  4. Обновите конфигурацию базы данных.

Чтение по типу корневого элемента:

URI = "http://example.com/goods";
ТипКорня = ФабрикаXDTO.Тип(URI, "Товары");
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл(ИмяФайла);
Данные = ФабрикаXDTO.ПрочитатьXML(Чтение, ТипКорня);
Чтение.Закрыть();

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

Менять конфигурацию ради одной схемы не обязательно. Фабрику можно собрать из XSD прямо в коде: прочитать схему через ПостроительDOM, получить объект СхемаXML методом ПостроительСхемXML.СоздатьСхемуXML(), добавить его в НаборСхемXML и создать Новый ФабрикаXDTO(НаборСхем). Это удобно во внешней обработке, которую нельзя встраивать в типовую конфигурацию.

Проверка структуры XML и обработка ошибок чтения

Чужой файл обязательно когда-нибудь придёт битым: оборванным при передаче, в другой кодировке, с изменённой структурой. Код должен сообщить об этом понятно, а не упасть с системным окном.

Базовая защита — обернуть разбор в Попытка:

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

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

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

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

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

Частые вопросы о чтении XML в 1С

Можно ли прочитать XML на клиенте, не передавая файл на сервер?
Объект ЧтениеXML доступен и на клиенте. Но писать результат в базу всё равно придётся на сервере, а в веб-клиенте прямого доступа к файлам нет. Разбор на сервере проще и надёжнее.
Чем глобальная функция ПрочитатьXML отличается от ФабрикаXDTO.ПрочитатьXML?
Глобальная ПрочитатьXML читает значения, ранее записанные функцией ЗаписатьXML в формате сериализации 1С (ссылки, структуры, таблицы). Произвольный чужой XML ей не подходит — для него нужна фабрика XDTO.
Как открыть XML-файл, чтобы просто посмотреть, что внутри?
Любым текстовым редактором или браузером. Браузер покажет дерево тегов и сразу сообщит, если разметка битая, — это быстрый способ проверить файл до загрузки.
Сколько весит XML, который ещё можно читать через DOM?
Жёсткой границы нет, всё упирается в память рабочего процесса. Файлы в единицы мегабайт DOM читает без проблем; от сотни мегабайт и выше безопаснее потоковое чтение.
Как прочитать XML из архива ZIP?
Сначала распакуйте архив объектом ЧтениеZipФайла во временный каталог, затем читайте извлечённый XML как обычный файл и удалите временные файлы после разбора.