XML-строка в 1С 8.3: как разобрать, собрать и не сломать данные

· 10 мин

Содержание

Чтобы обработать в 1С текст как XML, файл не нужен: строку передают в ЧтениеXML.УстановитьСтроку(Текст) и читают узлы циклом Прочитать(). Обратная задача решается так же: ЗаписьXML.УстановитьСтроку("UTF-8"), затем запись элементов, и метод Закрыть() возвращает готовый XML-текст. Временные файлы на диске сервера при этом не создаются.

Строку с XML приходится обрабатывать постоянно: ответ веб-сервиса, тело HTTP-запроса, поле с настройками в справочнике, сообщение из очереди обмена. Для этого в платформе есть четыре инструмента: потоковое чтение и запись, DOM, XDTO и XSLT. Каждый работает со строкой без промежуточного файла.

Как XML-текст ложится на объекты платформы 1С

XML — это дерево. У корня есть дочерние элементы, у элементов — атрибуты и текст. В 1С это дерево можно получить четырьмя наборами объектов, и выбор между ними определяет весь дальнейший код.

ИнструментОбъекты 1СКогда подходитМинус
Потоковое чтение и записьЧтениеXML, ЗаписьXMLБольшие тексты, простая структура, максимум скоростиНет произвольного доступа, код длиннее
DOMПостроительDOM, ДокументDOM, ЗаписьDOMНужно искать узлы по имени, XPath, править документВесь документ в памяти
XDTOФабрикаXDTO, ОбъектXDTO, XDTO-пакетыЕсть XSD-схема формата, обмен с внешней системойНужна схема или аккуратность с типами
XSLTПреобразованиеXSLПеревести один XML-формат в другойЛогику пишут на XSLT, а не на встроенном языке

Все четыре варианта умеют работать со строкой напрямую. У ЧтениеXML и ЗаписьXML для этого есть УстановитьСтроку, у ПреобразованиеXSL — методы ЗагрузитьИзСтроки и ПреобразоватьИзСтроки. DOM и XDTO читают текст через тот же ЧтениеXML.

Соответствие понятий, которое пригодится при чтении кода:

  • 🏷️ элемент <Товар> — узел с типом ТипУзлаXML.НачалоЭлемента, имя в свойстве Имя;
  • 🔤 текст между тегами — узел ТипУзлаXML.Текст, содержимое в свойстве Значение;
  • 📎 атрибут Код="001" — читается методом ЗначениеАтрибута("Код");
  • 🔚 закрывающий тег — узел ТипУзлаXML.КонецЭлемента.

Всё, что лежит в XML, — строки. Число 350.00 и дата 2026-10-07T00:00:00 приходят текстом. Переводите их в типы 1С функцией XMLЗначение, а не Число() или Дата(): она понимает формат XML Schema с точкой в дробной части и буквой T в дате.

Разбор XML-строки в 1С: потоковое чтение

Возьмём строку с двумя товарами и превратим её в таблицу значений. В 1С многострочный литерал начинается с | на каждой новой строке, а кавычки внутри удваиваются.

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

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

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

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

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

Чтение.Закрыть();

После выполнения в таблице две строки: 001 | Бумага А4 | 350 и 002 | Скрепки | 45,5.

Что здесь важно:

  1. Переменная ИмяПоля запоминает, внутри какого элемента мы стоим. Без неё текст «Бумага А4» не к чему привязать.
  2. На КонецЭлемента имя сбрасывается. Иначе пробелы и переводы строк между тегами могут попасть не в то поле.
  3. Если атрибута нет, ЗначениеАтрибута вернёт Неопределено. Для обязательных атрибутов проверяйте это явно.
  4. Закрыть() освобождает объект. В длинных обработках не оставляйте ридеры открытыми.

Пробелы и переводы строк между тегами

Отступы в «красивом» XML — тоже текст. Если ваш код реагирует на любой узел Текст, он получит пустые строки из пробелов. Защита простая: обрабатывать текст только когда известен ИмяПоля, как в примере, либо отбрасывать значения, у которых СокрЛП(Чтение.Значение) = "".

Строка пришла как двоичные данные

Тело HTTP-ответа удобнее сразу брать строкой: HTTPОтвет.ПолучитьТелоКакСтроку("UTF-8"). Если же на руках ДвоичныеДанные, переведите их функцией ПолучитьСтрокуИзДвоичныхДанных(Данные, "UTF-8") и только потом передавайте в УстановитьСтроку. Кодировку указывайте ту, в которой текст реально пришёл, а не ту, что написана в объявлении.

Формирование XML-текста в 1С через ЗаписьXML

Запись идёт сверху вниз: открыли элемент, добавили атрибуты, записали текст или вложенные элементы, закрыли. Каждому ЗаписатьНачалоЭлемента нужен свой ЗаписатьКонецЭлемента.

Параметры = Новый ПараметрыЗаписиXML("UTF-8", "1.0", Истина);

Запись = Новый ЗаписьXML;
Запись.УстановитьСтроку(Параметры);
Запись.ЗаписатьОбъявлениеXML();

Запись.ЗаписатьНачалоЭлемента("Заказ");
Запись.ЗаписатьАтрибут("Номер", "А-15");
Запись.ЗаписатьАтрибут("Дата", XMLСтрока(ТекущаяДатаСеанса()));

Запись.ЗаписатьНачалоЭлемента("Покупатель");
Запись.ЗаписатьТекст("ООО «Рога & Копыта»");
Запись.ЗаписатьКонецЭлемента(); // Покупатель

Запись.ЗаписатьНачалоЭлемента("Сумма");
Запись.ЗаписатьТекст(XMLСтрока(12500.75));
Запись.ЗаписатьКонецЭлемента(); // Сумма

Запись.ЗаписатьКонецЭлемента(); // Заказ

ТекстXML = Запись.Закрыть();

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

Чем этот способ лучше склейки строк:

  • ✅ амперсанд из названия покупателя превратится в &amp;, а знак < — в &lt;, документ останется корректным;
  • ✅ XMLСтрока(12500.75) даст 12500.75 с точкой, без пробелов между разрядами, которые вставил бы Строка() на русской локали;
  • ✅ дата запишется в формате 2026-10-07T14:30:00, который понимает любая внешняя система.

Если нужен блок, который нельзя экранировать (например, вложенный HTML), используйте ЗаписатьСекциюCDATA(Текст). А готовый кусок XML, уже собранный и проверенный в другом месте, вставляют методом ЗаписатьБезОбработки — но тогда за корректность этого куска отвечаете вы.

Пространства имён

Внешние форматы почти всегда требуют пространство имён. Объявите его сразу после открытия корневого элемента:

Запись.ЗаписатьНачалоЭлемента("Документ", "urn:example:orders");
Запись.ЗаписатьСоответствиеПространстваИмен("", "urn:example:orders");

При чтении такого документа свойство Имя вернёт имя с префиксом, если он есть. Для сравнения надёжнее брать ЛокальноеИмя и проверять URIПространстваИмен.

Ошибки при разборе XML-строки в 1С

Любая ошибка разбора всплывает как исключение на вызове Прочитать(). В тексте сообщения платформа указывает позицию в квадратных скобках — строку и столбец, где разбор остановился. С этой позиции и начинайте поиск.

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

Чтобы ошибка в одном сообщении не роняла всю обработку, оборачивайте разбор в Попытка … Исключение:

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

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

XML-текст в обмене между 1С и внешними системами

Строковый XML — основной носитель данных в интеграциях. Типичные точки, где он появляется:

  • 🌐 HTTP-сервисы и HTTP-запросы. Тело запроса задают HTTPЗапрос.УстановитьТелоИзСтроки(ТекстXML, "UTF-8"), заголовок Content-Type выставляют application/xml или text/xml — как требует принимающая сторона.
  • 🧩 Веб-сервисы SOAP. Конверт формирует сама платформа через WSПрокси, но иногда сервис отдаёт внутри поля ещё одну XML-строку. Её разбирают отдельным ЧтениеXML.
  • 📨 Очереди и шины. Сообщение приходит строкой, и его нельзя писать на диск по требованиям безопасности — здесь УстановитьСтроку незаменим.
  • 💾 Хранение в реквизите. Настройки или снимок документа кладут в реквизит типа «Строка неограниченной длины». Удобно, но по такому полю нельзя быстро искать запросом.

Для обмена между конфигурациями на одной платформе формат обычно не изобретают. Типовые решения используют универсальный формат EnterpriseData и правила конвертации, а свой XML нужен, когда с другой стороны не 1С.

Перед стартом интеграции согласуйте с другой стороной пять вещей:

  1. Кодировку (почти всегда UTF-8).
  2. Формат чисел и дат.
  3. Наличие пространства имён и его URI.
  4. XSD-схему, если она есть.
  5. Что делать с пустыми значениями: пропускать элемент или писать пустой тег.
Перед отправкой XML во внешнюю систему
Выполнено: 0 / 5

Работа с XML через DOM, XDTO и XSLT

Потокового чтения хватает для простых структур. Когда документ сложный или есть схема, удобнее другие инструменты.

DOM: найти узлы по имени

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

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

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

Изменённый ДокументDOM превращают обратно в строку через ЗаписьDOM:

Запись = Новый ЗаписьXML;
Запись.УстановитьСтроку("UTF-8");
ЗаписьDOM = Новый ЗаписьDOM;
ЗаписьDOM.Записать(Документ, Запись);
ТекстXML = Запись.Закрыть();

Для выборок посложнее у ДокументDOM есть метод ВычислитьВыражениеXPath. Если в документе есть пространство имён, ему нужен разыменователь Новый РазыменовательПространствИменDOM(Документ) — без него XPath с префиксами ничего не найдёт.

XDTO: строка по схеме

Если внешняя система дала XSD, её импортируют в конфигурацию как XDTO-пакет (в Конфигураторе, ветка Общие → XDTO-пакеты). Дальше строка читается в типизированный объект:

Чтение = Новый ЧтениеXML;
Чтение.УстановитьСтроку(Текст);
ТипЗаказа = ФабрикаXDTO.Тип("urn:example:orders", "Заказ");
Заказ = ФабрикаXDTO.ПрочитатьXML(Чтение, ТипЗаказа);
Покупатель = Заказ.Покупатель;

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

Отдельный случай — сериализация значений самой 1С. Глобальные ЗаписатьXML(Запись, Значение) и ПрочитатьXML(Чтение) переводят в XML и обратно структуры, массивы, таблицы значений и ссылки. Такой текст понимает только другая база 1С, для внешних систем он не годится.

XSLT: один формат в другой

Преобразование = Новый ПреобразованиеXSL;
Преобразование.ЗагрузитьИзСтроки(ТекстXSL);
ТекстРезультат = Преобразование.ПреобразоватьИзСтроки(ТекстXML);

Шаблон XSL удобно хранить в макете обработки типа «Текстовый документ» и получать его текст при вызове. XSLT выручает, когда партнёр меняет формат: правите шаблон, а код на встроенном языке не трогаете.

Готовые приёмы для повседневных задач

Проверить, что строка вообще XML. Самый честный способ — попытаться прочитать её целиком:

Функция ЭтоКорректныйXML(Текст) Экспорт
    Попытка
        Чтение = Новый ЧтениеXML;
        Чтение.УстановитьСтроку(Текст);
        Пока Чтение.Прочитать() Цикл
        КонецЦикла;
        Чтение.Закрыть();
        Возврат Истина;
    Исключение
        Возврат Ложь;
    КонецПопытки;
КонецФункции

Отформатировать «слипшийся» XML для чтения. Прочитайте строку в DOM, как в примере выше, и запишите через ЗаписьDOM в ЗаписьXML, открытый с Новый ПараметрыЗаписиXML("UTF-8", "1.0", Истина). На выходе — тот же документ с отступами.

Получить значение одного элемента без цикла по всему документу. Для маленьких ответов (статус, номер, код ошибки) DOM и ПолучитьЭлементыПоИмени("Статус") дают результат в три строки. Для текстов на сотни мегабайт так делать не надо — берите потоковое чтение и выходите из цикла, как только нашли нужный узел.

Сохранить строку в файл, когда всё-таки нужен файл. Не пишите текст через ТекстовыйДокумент с другой кодировкой, чем в объявлении. Надёжнее собрать документ сразу в файл: ЗаписьXML.ОткрытьФайл(ИмяФайла, "UTF-8") вместо УстановитьСтроку.

ЗадачаЧем решать
Ответ сервиса на 2–3 поляDOM, ПолучитьЭлементыПоИмени
Выгрузка тысяч строкЗаписьXML потоком
Загрузка большого файлаЧтениеXML потоком
Формат партнёра с XSDXDTO-пакет
Смена структуры без правки кодаXSLT
Передать данные в другую базу 1ССериализация ЗаписатьXML / ПрочитатьXML

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

Работает ли ЧтениеXML.УстановитьСтроку на клиенте?
Да, объекты ЧтениеXML и ЗаписьXML доступны и на клиенте, и на сервере. Но большие тексты и обращения к внешним системам лучше обрабатывать на сервере, чтобы не гонять данные по сети.
Есть ли ограничение на длину XML-строки?
Жёсткого лимита на уровне метода нет, упираетесь вы в память процесса. Текст на сотни мегабайт лучше читать потоково из файла, а не держать целиком в переменной и тем более не грузить в DOM.
Почему в результате ЗаписьXML нет объявления xml?
Объявление пишется только явным вызовом ЗаписатьОбъявлениеXML() сразу после УстановитьСтроку. Если его не вызвать, текст начнётся сразу с корневого элемента — для некоторых получателей это допустимо.
Можно ли хранить XML в реквизите справочника?
Можно, в реквизите типа «Строка» неограниченной длины. Учтите, что по такому полю нельзя отбирать в запросах так же, как по обычному реквизиту, поэтому ключевые значения лучше дублировать отдельными реквизитами.
Чем XMLСтрока отличается от Строка?
XMLСтрока выдаёт значение в формате XML Schema: число с точкой и без разделителей разрядов, дату вида ГГГГ-ММ-ДДTчч:мм:сс, булево как true или false. Строка зависит от региональных настроек и для обмена не подходит.