Чтение XML в 1С 8.3: от потокового разбора до XPath-выборки с примерами кода

· 9 мин

Содержание

Читать XML файл в 1С проще всего объектом ЧтениеXML: открываете файл методом ОткрытьФайл() и в цикле Пока Чтение.Прочитать() Цикл разбираете узлы по типу и имени. Если файл небольшой и нужна навигация по структуре, передайте то же ЧтениеXML в ПостроительDOM и выбирайте узлы XPath-выражениями.

Каждый способ показан рабочим кодом на одном и том же файле-примере. Отдельно разобраны три места, где разработчики чаще всего застревают: файл лежит на компьютере пользователя, а код выполняется на сервере; файл в кодировке windows-1251 без объявления; XPath ничего не находит из-за пространства имён.

Все примеры написаны для платформы 1С:Предприятие 8.3 и работают в любой конфигурации. Код размещайте во внешней обработке или в модуле формы, в процедурах с директивой &НаСервере.

Файл-пример, на котором проверяется весь код:

<?xml version="1.0" encoding="UTF-8"?>
<Прайс Дата="2026-10-01">
  <Товар Код="001">
    <Наименование>Бумага А4, 500 листов</Наименование>
    <Цена>450.00</Цена>
  </Товар>
  <Товар Код="002">
    <Наименование>Картридж лазерный</Наименование>
    <Цена>3200.00</Цена>
  </Товар>
</Прайс>

Чем читать XML в 1С: четыре инструмента платформы

В 1С 8.3 есть четыре механизма чтения XML. Они не исключают друг друга: ЧтениеXML служит входной точкой и для DOM, и для XDTO.

ИнструментКак работаетКогда выбиратьОграничения
ЧтениеXMLИдёт по файлу узел за узлом, вперёдБольшие файлы, простая плоская структураНазад вернуться нельзя, логику обхода пишете сами
ПостроительDOM + ДокументDOMЗагружает весь документ в память деревомФайлы до десятков мегабайт, сложная вложенностьПамять: дерево занимает в разы больше самого файла
XPath (ВычислитьВыражениеXPath)Запрос к DOM-дереву одной строкойНужны узлы по условию, по атрибутуТребует DOM, чувствителен к пространствам имён
ФабрикаXDTO.ПрочитатьXML()Превращает XML в ОбъектXDTOЕсть XSD-схема или пакет XDTO в конфигурацииБез схемы типы значений приходят строками

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

Правило выбора простое. Файл больше 50–100 МБ или неизвестного размера — только ЧтениеXML. Нужен поиск по условию и файл небольшой — DOM с XPath.

Как читать XML файл потоком через ЧтениеXML

ЧтениеXML не держит документ в памяти. Метод Прочитать() переходит к следующему узлу и возвращает Ложь, когда файл кончился. Тип текущего узла смотрят в свойстве ТипУзла, имя — в Имя, текст — в Значение.

Для разбора прайса достаточно трёх типов узлов:

  • 🟢 ТипУзлаXML.НачалоЭлемента — открывающий тег, здесь же читаются атрибуты;
  • 📝 ТипУзлаXML.Текст — содержимое между тегами;
  • 🔴 ТипУзлаXML.КонецЭлемента — закрывающий тег, сигнал «запись закончена».

Код читает файл в таблицу значений:

&НаСервере
Функция ПрочитатьПрайс(ИмяФайла)

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

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

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

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

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

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

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

    КонецЦикла;

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

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

Что здесь стоит запомнить:

  1. Атрибуты читаются, пока курсор стоит на начале элемента. После следующего Прочитать() они уже недоступны.
  2. ПолучитьАтрибут() возвращает Неопределено, если атрибута нет. Колонка с типом «Строка» превратит это в пустую строку, но в своих проверках учитывайте такой случай.
  3. Переменная ТекущееПоле сбрасывается на закрывающем теге. Иначе пробелы и переводы строк между тегами попадут не в то поле.
  4. Число("450.00") понимает точку как разделитель дробной части. Если поставщик пишет цену с запятой, сначала замените её: СтрЗаменить(Чтение.Значение, ",", ".").

Когда целую ветку нужно проигнорировать (например, огромный блок <Картинки> внутри товара), вызовите Чтение.Пропустить() на её начале. Курсор перескочит сразу за закрывающий тег, вложенные узлы разбирать не придётся.

Разбор XML в 1С через DOM-дерево

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

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

Корень = Документ.ДокументЭлемент;
Сообщить("Прайс на дату: " + Корень.ПолучитьАтрибут("Дата"));

Узлы = Документ.ПолучитьЭлементыПоИмени("Товар");
Для Индекс = 0 По Узлы.Количество() - 1 Цикл

    Узел = Узлы.Элемент(Индекс);
    Код = Узел.ПолучитьАтрибут("Код");
    Наименование = "";
    Цена = 0;

    Дочерний = Узел.ПервыйДочерний;
    Пока Дочерний <> Неопределено Цикл
        Если Дочерний.ТипУзла = ТипУзлаDOM.Элемент Тогда
            Если Дочерний.ИмяУзла = "Наименование" Тогда
                Наименование = Дочерний.ТекстовоеСодержимое;
            ИначеЕсли Дочерний.ИмяУзла = "Цена" Тогда
                Цена = Число(Дочерний.ТекстовоеСодержимое);
            КонецЕсли;
        КонецЕсли;
        Дочерний = Дочерний.СледующийСоседний;
    КонецЦикла;

    Сообщить(Код + " | " + Наименование + " | " + Формат(Цена, "ЧДЦ=2"));

КонецЦикла;

Ключевые свойства DOM, которые понадобятся почти в любой задаче:

  • 🌳 ДокументЭлемент — корневой элемент документа;
  • 🔎 ПолучитьЭлементыПоИмени() — все элементы с таким именем на любой глубине;
  • ↘️ ПервыйДочерний и СледующийСоседний — обход потомков по одному;
  • 📄 ТекстовоеСодержимое — весь текст внутри элемента, включая вложенные;
  • 🏷️ ПолучитьАтрибут() — значение атрибута; если атрибута нет, вернётся пустая строка.

Проверка ТипУзла = ТипУзлаDOM.Элемент в цикле обязательна. Переводы строк и отступы между тегами — тоже узлы, только текстовые. Без проверки код будет искать «Наименование» среди пробелов.

Выборка данных XPath-запросами

XPath избавляет от ручного обхода. Одна строка выражения заменяет вложенные циклы и условия. Работает только поверх ДокументDOM, поэтому сначала постройте документ, как в предыдущем разделе.

Пример: все товары дороже 1000 рублей.

Разыменователь = Новый РазыменовательПространствИменDOM(Документ);
Результат = Документ.ВычислитьВыражениеXPath(
    "/Прайс/Товар[number(Цена) > 1000]",
    Документ,
    Разыменователь,
    ТипРезультатаDOMXPath.УпорядоченныйИтераторУзлов);

Узел = Результат.ПолучитьСледующий();
Пока Узел <> Неопределено Цикл
    Сообщить(Узел.ПолучитьАтрибут("Код"));
    Узел = Результат.ПолучитьСледующий();
КонецЦикла;

Если нужно одно значение, а не набор узлов, меняйте тип результата:

// Наименование товара с кодом 002
Результат = Документ.ВычислитьВыражениеXPath(
    "string(/Прайс/Товар[@Код='002']/Наименование)",
    Документ, Разыменователь, ТипРезультатаDOMXPath.Строка);
Наименование = Результат.СтроковоеЗначение;

// Сколько всего товаров в файле
Результат = Документ.ВычислитьВыражениеXPath(
    "count(/Прайс/Товар)",
    Документ, Разыменователь, ТипРезультатаDOMXPath.Число);
Количество = Результат.ЧисловоеЗначение;

Шпаргалка по выражениям, которые закрывают большинство задач:

ВыражениеЧто выбирает
//ТоварВсе элементы «Товар» на любой глубине
/Прайс/Товар[1]Первый товар (нумерация в XPath с единицы)
//Товар[@Код='001']Товар с нужным значением атрибута
//Товар/@КодСами атрибуты «Код» всех товаров
//Товар[Цена > 1000]Товары, у которых дочерний элемент удовлетворяет условию
//Товар[last()]Последний товар

Из XML в таблицу значений и табличный документ

Чаще всего XML читают не ради самого разбора. Данные нужно показать пользователю или загрузить в справочники и документы. Промежуточным звеном удобно сделать таблицу значений: функция ПрочитатьПрайс() из раздела про ЧтениеXML уже её возвращает.

Для просмотра загруженного выведите таблицу в табличный документ. Макет не нужен, ячейки заполняются через Область():

&НаСервере
Функция ПрайсВТабличныйДокумент(Товары)

    ТабДок = Новый ТабличныйДокумент;
    Заголовки = ТабДок.Область(1, 1, 1, 3);
    ТабДок.Область(1, 1).Текст = "Код";
    ТабДок.Область(1, 2).Текст = "Наименование";
    ТабДок.Область(1, 3).Текст = "Цена";
    Заголовки.Шрифт = Новый Шрифт(, , Истина);

    НомерСтроки = 1;
    Для Каждого Строка Из Товары Цикл
        НомерСтроки = НомерСтроки + 1;
        ТабДок.Область(НомерСтроки, 1).Текст = Строка.Код;
        ТабДок.Область(НомерСтроки, 2).Текст = Строка.Наименование;
        ТабДок.Область(НомерСтроки, 3).Текст = Формат(Строка.Цена, "ЧДЦ=2");
    КонецЦикла;

    ТабДок.Область(1, 2, НомерСтроки, 2).ШиринаКолонки = 40;
    Возврат ТабДок;

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

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

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

Файл на компьютере пользователя, код на сервере: как передать XML

ОткрытьФайл() на сервере ищет файл в файловой системе сервера. Путь вида C:\Users\buh\Desktop\price.xml там не существует, и чтение упадёт. В тонком клиенте и веб-клиенте файл сначала передают на сервер через временное хранилище.

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

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

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

    Товары = ПрочитатьПрайс(ИмяВременного);
    ТаблицаПросмотра = ПрайсВТабличныйДокумент(Товары);

    УдалитьФайлы(ИмяВременного);
КонецПроцедуры

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

На свежих версиях платформы можно обойтись без временного файла: Чтение.ОткрытьПоток(Данные.ОткрытьПотокДляЧтения()). Если конфигурация должна работать на старых сборках 8.3, оставьте вариант с временным файлом, он работает везде.

Кодировка, пространства имён и ошибки разбора XML

Парсер 1С берёт кодировку из объявления <?xml ... encoding="..."?>. Если объявления нет, файл считается UTF-8. Выгрузки старых программ часто приходят в windows-1251 без объявления, и разбор падает на первой же русской букве.

Решение: прочитать файл как текст в правильной кодировке и передать строку парсеру.

Текст = Новый ЧтениеТекста(ИмяФайла, "windows-1251");
Содержимое = Текст.Прочитать();
Текст.Закрыть();

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

Вторая частая ловушка — пространство имён по умолчанию. Файл начинается так: <Прайс xmlns="urn:example:price">. Код с ПолучитьЭлементыПоИмени("Товар") и XPath /Прайс/Товар вдруг ничего не находит, хотя товары в файле есть. Причина: у каждого элемента теперь полное имя с URI пространства.

Для XPath задайте префикс явно и используйте его в выражении:

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

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

Ошибки разбора не должны останавливать загрузку целиком без объяснения. Оберните чтение в Попытка ... Исключение и покажите пользователю текст ошибки через ОписаниеОшибки(): парсер сообщает номер строки и позицию, где споткнулся.

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

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

Можно ли прочитать XML на клиенте, без вызова сервера?
Да, ЧтениеXML и DOM доступны и на клиенте. Но запись в справочники и документы всё равно идёт на сервере, поэтому разбирать файл обычно удобнее там же.
Чем открыть XML-файл, чтобы просто посмотреть содержимое?
Подойдёт любой браузер или текстовый редактор с подсветкой синтаксиса. Браузер сразу покажет, если файл некорректен, и укажет строку с ошибкой.
Как прочитать XML, который выгрузила сама 1С через ЗаписатьXML?
Откройте его в ЧтениеXML, перейдите к первому элементу методом ПерейтиКСодержимому() и вызовите глобальную функцию ПрочитатьXML(Чтение). Она вернёт объект платформы того же типа, что выгружали.
Нужна ли XSD-схема для чтения XML в 1С?
Нет. ЧтениеXML, DOM и XPath работают без схемы. Схема нужна только для XDTO, если хотите получить типизированные объекты и проверку структуры.
Как читать XML с несколькими одинаковыми вложенными тегами?
В DOM они приходят списком из ПолучитьЭлементыПоИмени() или XPath-итератором. В потоковом разборе на каждое начало такого элемента добавляйте новую строку в таблицу или подчинённую табличную часть.