Как прочитать XML из текста в 1С 8.3
Содержание
Чтобы прочитать XML из текста в 1С 8.3, создайте объект ЧтениеXML, передайте строку методом УстановитьСтроку() и обходите документ в цикле Пока ЧтениеXML.Прочитать() Цикл. Внутри цикла смотрите на свойство ТипУзла: по нему отличают начало элемента от текста и конца элемента.
Разобрать XML из строки можно тремя способами. Построчное чтение через ЧтениеXML — для потоковой обработки больших документов. Разбор в дерево ДокументDOM с выборкой через XPath — когда нужно вытащить отдельные ветки. Десериализация ФабрикаXDTO/ДесериализаторXDTO — когда XML отражает известную структуру объекта. Ниже — рабочий код по каждому и типичные ошибки парсера.
Что выбрать: построчное чтение, DOM или XDTO
Способ зависит от размера документа и от того, что вы из него берёте.
ЧтениеXML в цикле Прочитать(). Память не растёт — узлы обрабатываются по одному. Подходит для выгрузок на сотни тысяч строк.ДокументDOM и применяйте XPath. Дерево целиком загружается в память, зато выборка пишется одной строкой выражения.ДесериализаторXDTO или Сериализатор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: " + Текст;
КонецПопытки;
Что чаще всего ломает парсер и как это увидеть:
<?xml или первым тегом есть лишние символы: пробел, BOM, кусок HTTP-заголовка. Обрежьте строку до первого <.<a><b></a></b>). XML не починится сам — исправляйте на стороне отправителя.&, < или >. В валидном XML это &, <, >. Либо данные не экранированы, либо строка обрезана посреди сущности.encoding кодировка не совпадает с реальной. Приведите строку к кодировке из объявления или исправьте объявление.Проверять «валидность» отдельным проходом обычно не нужно: если документ битый, Прочитать() сам остановится на ошибке. Для проверки на соответствие XSD-схеме применяют отдельный механизм проверки по схеме, но это уже не про «прочитать из строки».
Частые вопросы
Чем `УстановитьСтроку()` отличается от `ОткрытьФайл()`?
УстановитьСтроку() читает XML из строки в памяти, ОткрытьФайл() — напрямую из файла на диске. Для данных из HTTP-ответа или поля базы берут строку; для больших выгрузок с диска — файл, чтобы не держать весь текст в памяти.Как прочитать значение внутри вложенного тега, не запутавшись в уровнях?
НачалоЭлемента запоминайте текущий тег, по КонецЭлемента сбрасывайте. Если вложенность глубокая и условий много — переходите на ДокументDOM и XPath, там путь к узлу задаётся одним выражением.Можно ли получить XML как строку целиком, а не по узлам?
ЧтениеXML отдаёт документ только по узлам. Чтобы получить поддерево как текст, разберите его в ДокументDOM и сериализуйте нужный узел обратно через ЗаписьXML. Но если цель — просто вытащить значения, обходить узлы проще, чем гонять текст туда-обратно.Почему `Значение` пустое, хотя тег с текстом?
Текст, он идёт следующим после НачалоЭлемента. На самом открывающем теге Значение всегда пустое. Читайте Значение только когда ТипУзла = ТипУзлаXML.Текст.Как достать атрибут по имени, не перебирая все?
ЗначениеАтрибута("ИмяАтрибута"), вызванный на узле НачалоЭлемента. Если атрибута нет, вернётся пустая строка — эту ситуацию обрабатывайте явно.