Чтение XML в 1С 8.3: от потокового разбора до XPath-выборки с примерами кода
Содержание
Читать 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.КонецЭлемента Тогда
ТекущееПоле = "";
Если Чтение.Имя = "Товар" Тогда
ТекущаяСтрока = Неопределено;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Чтение.Закрыть();
Возврат Товары;
КонецФункции
Что здесь стоит запомнить:
- Атрибуты читаются, пока курсор стоит на начале элемента. После следующего
Прочитать()они уже недоступны. ПолучитьАтрибут()возвращаетНеопределено, если атрибута нет. Колонка с типом «Строка» превратит это в пустую строку, но в своих проверках учитывайте такой случай.- Переменная
ТекущееПолесбрасывается на закрывающем теге. Иначе пробелы и переводы строк между тегами попадут не в то поле. Число("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 с несколькими одинаковыми вложенными тегами?
ПолучитьЭлементыПоИмени() или XPath-итератором. В потоковом разборе на каждое начало такого элемента добавляйте новую строку в таблицу или подчинённую табличную часть.