Как прочитать XML из текста в 1С 8.3

· 9 мин

Содержание

Чтобы прочитать XML из текста в 1С 8.3, создайте объект ЧтениеXML, передайте строку методом УстановитьСтроку() и обходите документ в цикле Пока ЧтениеXML.Прочитать() Цикл. Внутри цикла смотрите на свойство ТипУзла: по нему отличают начало элемента от текста и конца элемента.

Разобрать XML из строки можно тремя способами. Построчное чтение через ЧтениеXML — для потоковой обработки больших документов. Разбор в дерево ДокументDOM с выборкой через XPath — когда нужно вытащить отдельные ветки. Десериализация ФабрикаXDTO/ДесериализаторXDTO — когда XML отражает известную структуру объекта. Ниже — рабочий код по каждому и типичные ошибки парсера.

Что выбрать: построчное чтение, DOM или XDTO

Способ зависит от размера документа и от того, что вы из него берёте.

Как разбирать XML в вашем случае?

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

Готовим строку и создаём объект чтения

XML может прийти откуда угодно: из ответа HTTP-сервиса, из поля документа, из файла, прочитанного в строку. Для ЧтениеXML это всегда просто текст.

Чаще всего строка берётся из ответа внешней системы. Тело HTTP-ответа получают так: СтрокаXML = Ответ.ПолучитьТелоКакСтроку(). Если контрагент отдаёт XML внутри поля JSON или в кодировке Base64, сначала распакуйте его в обычную строку, и только потом передавайте в УстановитьСтроку() — парсер ждёт чистый текст XML, без «обёрток».

Привяжите строку к объекту методом УстановитьСтроку():

СтрокаXML = "<Заказ Номер=""100""><Товар Код=""АА-01"">Болт</Товар></Заказ>";

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

Если XML лежит в файле, а не в строке, есть два пути:

  • 📄 Прочитать файл в строку (ЧтениеТекста или ТекстовыйДокument) и передать в УстановитьСтроку().
  • 📂 Открыть файл напрямую: ЧтениеXML.ОткрытьФайл(ПутьКФайлу) — удобно для больших выгрузок, строку в памяти держать не нужно.

Следите за кодировкой. Если в строке стоит объявление <?xml version="1.0" encoding="windows-1251"?>, а фактически текст в UTF-8, парсер отдаст «кракозябры» или упадёт. Кодировка объявления должна совпадать с реальной.

Строку с XML не склеивайте из кусков вручную и не правьте регулярками — сломаете экранирование. Для формирования используйте ЗаписьXML, для разбора — ЧтениеXML.

Как устроен обход: типы узлов

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

Узел — это не только тег. Начало тега, его текст и закрывающий тег — три разных узла. Какой именно сейчас под курсором, показывает свойство ТипУзла.

Основные значения перечисления ТипУзлаXML:

Тип узлаКогда возникаетЧто читать
НачалоЭлементаоткрывающий тег <Товар>Имя, атрибуты
КонецЭлементазакрывающий тег </Товар>Имя
Тексттекст внутри тегаЗначение
НачалоДокумента / КонецДокументаграницы документа—

Каркас обхода почти всегда один:

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

Текст элемента приходит отдельным узлом Текст уже после НачалоЭлемента. Частая ошибка — пытаться прочитать Значение на самом открывающем теге: там оно пустое.

Ещё одна засада ждёт, когда XML отформатирован с отступами. Между тегами, записанными «в столбик» с переносами строк, стоят невидимые символы — пробелы и переводы строки. Парсер отдаёт их отдельными узлами, и ваша проверка «если текст — пиши в поле» начнёт писать туда пустые строки и переносы.

Отфильтровать такой «воздух» просто — проверкой на непустое содержимое после обрезки:

ИначеЕсли ЧтениеXML.ТипУзла = ТипУзлаXML.Текст Тогда
    Значение = СокрЛП(ЧтениеXML.Значение);
    Если Значение = "" Тогда
        Продолжить; // пробелы и переносы между тегами — пропускаем
    КонецЕсли;
    // здесь Значение — реальный текст элемента
КонецЕсли;

СокрЛП() убирает ведущие и хвостовые пробелы, после чего пустая строка — верный признак «воздуха» между тегами. Так в данные попадёт только значащий текст.

Извлечение значений и атрибутов

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

Перебрать все атрибуты текущего тега помогает метод ПрочитатьАтрибут() — он работает так же, как Прочитать(), но по атрибутам:

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

Если нужен конкретный атрибут по имени, не перебирая все, используйте ЗначениеАтрибута():

Код = ЧтениеXML.ЗначениеАтрибута("Код");

Метод вернёт пустую строку, если атрибута с таким именем у тега нет, — проверку на "" закладывайте сразу.

ПрочитатьАтрибут() сдвигает внутренний указатель атрибутов. Если сначала перебрали атрибуты циклом, а потом вызвали ЗначениеАтрибута() на том же теге — поведение зависит от позиции. Выбирайте один подход на тег: либо перебор, либо точечное чтение по имени.

Готовый пример: читаем XML из строки целиком

Соберём разбор в одну функцию. На входе — строка XML с заказом и строками товаров, на выходе — таблица значений со столбцами.

Функция ПрочитатьЗаказИзСтроки(СтрокаXML)

    Таблица = Новый ТаблицаЗначений;
    Таблица.Колонки.Добавить("Код");
    Таблица.Колонки.Добавить("Наименование");

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

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

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

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

            ТекущаяСтрока = Таблица.Добавить();
            ТекущаяСтрока.Код = ЧтениеXML.ЗначениеАтрибута("Код");

        ИначеЕсли ЧтениеXML.ТипУзла = ТипУзлаXML.Текст
            И ТекущаяСтрока <> Неопределено Тогда

            ТекущаяСтрока.Наименование = ЧтениеXML.Значение;

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

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

        КонецЕсли;

    КонецЦикла;

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

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

Логика простая: на открытии <Товар> заводим новую строку и берём атрибут Код; текстовый узел пишем в Наименование; на закрытии </Товар> сбрасываем ссылку, чтобы чужой текст не попал в строку. Переменная ТекущаяСтрока играет роль контекста — без неё текст из соседних тегов перемешается.

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

Выборка по XPath через ДокументDOM

Когда структура глубокая и нужны не все узлы, а, скажем, «все теги Товар внутри Заказ», построчный обход превращается в лес вложенных условий. Здесь выручает XPath.

Сначала разберите тот же ЧтениеXML в объект ДокументDOM построителем ПостроительDOM:

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

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

Теперь документ целиком в памяти в виде дерева. Выражение XPath вычисляет метод ВычислитьВыражениеXPath(), он возвращает результат, по которому идут методом ПолучитьСледующий():

Результат = ДокументDOM.ВычислитьВыражениеXPath("//Товар", ДокументDOM);

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

Полезные выражения XPath для частых задач:

  • 🔹 //Товар — все теги Товар на любом уровне.
  • 🔹 /Заказ/Товар — только прямые дочерние Товар у корня Заказ.
  • 🔹 //Товар[@Код='АА-01'] — товар с конкретным атрибутом.
  • 🔹 count(//Товар) — число элементов (результат-число).

Если в XML объявлены пространства имён (атрибут xmlns), голые имена в XPath перестают находить узлы. Тогда нужен разыменователь пространств имён — передавайте его третьим параметром в ВычислитьВыражениеXPath(). Без пространств имён параметр не нужен.

Результат XPath бывает разным: набор узлов (//Товар), число (count(...)), строка или логическое значение. Для набора узлов идут циклом через ПолучитьСледующий(), а число или строку забирают сразу из свойства результата — обходить нечего.

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

Разбор через десериализацию XDTO

Если XML сформирован самой 1С (ЗаписьXML + сериализатор) или по известной XDTO-схеме, обходить узлы руками не нужно. Платформа соберёт объект сама.

Для стандартной сериализации объектов конфигурации:

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

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

На выходе — восстановленный объект (элемент справочника, документ, структура) того типа, что был записан. Это самый короткий путь для обмена «1С ↔ 1С».

Для разбора по произвольной XDTO-схеме применяют ФабрикаXDTO.ПрочитатьXML(), передавая тип из нужного пакета. Такой подход удобен при обмене с внешними системами, где схема XSD известна заранее.

Выбор между способами прост: свой формат от стороннего контрагента — ЧтениеXML или XPath; сериализация объектов 1С — СериализаторXDTO; строгая схема XSD — ФабрикаXDTO.

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

Чужой XML почти никогда не бывает идеальным: обрезанный файл, лишний символ до <?xml, неэкранированный &, несовпадающие теги. Любая из этих проблем роняет Прочитать() с исключением. Поэтому разбор всегда оборачивают в Попытка:

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

Что чаще всего ломает парсер и как это увидеть:

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

Проверять «валидность» отдельным проходом обычно не нужно: если документ битый, Прочитать() сам остановится на ошибке. Для проверки на соответствие XSD-схеме применяют отдельный механизм проверки по схеме, но это уже не про «прочитать из строки».

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

Чем `УстановитьСтроку()` отличается от `ОткрытьФайл()`?
УстановитьСтроку() читает XML из строки в памяти, ОткрытьФайл() — напрямую из файла на диске. Для данных из HTTP-ответа или поля базы берут строку; для больших выгрузок с диска — файл, чтобы не держать весь текст в памяти.
Как прочитать значение внутри вложенного тега, не запутавшись в уровнях?
При построчном чтении храните «контекст» в переменной: по НачалоЭлемента запоминайте текущий тег, по КонецЭлемента сбрасывайте. Если вложенность глубокая и условий много — переходите на ДокументDOM и XPath, там путь к узлу задаётся одним выражением.
Можно ли получить XML как строку целиком, а не по узлам?
ЧтениеXML отдаёт документ только по узлам. Чтобы получить поддерево как текст, разберите его в ДокументDOM и сериализуйте нужный узел обратно через ЗаписьXML. Но если цель — просто вытащить значения, обходить узлы проще, чем гонять текст туда-обратно.
Почему `Значение` пустое, хотя тег с текстом?
Текст элемента — это отдельный узел типа Текст, он идёт следующим после НачалоЭлемента. На самом открывающем теге Значение всегда пустое. Читайте Значение только когда ТипУзла = ТипУзлаXML.Текст.
Как достать атрибут по имени, не перебирая все?
Метод ЗначениеАтрибута("ИмяАтрибута"), вызванный на узле НачалоЭлемента. Если атрибута нет, вернётся пустая строка — эту ситуацию обрабатывайте явно.