Как преобразовать произвольный XML-ответ сервера в структуру или объект в 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При интеграции 1С с внешними веб-сервисами (будь то REST API или SOAP) разработчики регулярно сталкиваются с необходимостью обработки входящих XML-пакетов. Часто требуется преобразовать полученный XML-документ в привычную структуру 1С или быстро извлечь из него конкретные значения, например, идентификаторы, статусы выполнения операций или списки номенклатуры.

В этой статье мы подробно проанализируем, почему стандартные инструменты сериализации 1С могут выдавать ошибки при работе со сторонними XML-данными, и детально разберем четыре практических метода преобразования XML в структуру или объектные модели 1С.

Почему возникает ошибка при использовании Сериализатора XDTO?

Типичная ошибка начинающих разработчиков — попытка прочитать произвольный XML-ответ от сервера с помощью следующего кода:


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

При выполнении этого фрагмента платформа генерирует ошибку: "Ошибка преобразования данных XDTO: НачалоСвойства...". Давайте выясним причину такого поведения системы.

Метод СериализаторXDTO.ПрочитатьXML() с явным указанием типа Структура (или любого другого типа коллекции 1С) предназначен исключительно для десериализации XML-данных, которые ранее были сформированы и записаны самой платформой 1С. Такой XML должен иметь строго определенную структуру элементов (с использованием пространства имен http://v8.1c.ru/8.1/data/core, тегов <Structure>, <Property> и т.д.).

Если сервер присылает произвольный XML-документ (например, SOAP-конверт или кастомный XML-ответ), СериализаторXDTO не может сопоставить его теги со свойствами внутренней структуры 1С, что и приводит к аварийному завершению операции. Для работы с произвольными XML-документами необходимо применять другие механизмы.

---

Метод 1. Точечное извлечение данных с помощью DOM и XPath

Если вам не нужно преобразовывать весь XML-документ целиком, а требуется лишь получить одно или несколько конкретных значений (например, значение тега SKU_CreateUpdateResult), использование полной десериализации избыточно. В таких ситуациях наиболее эффективным решением будет использование объектной модели документа (DOM).

Рассмотрим базовый вариант получения значения по имени тега с помощью метода ПолучитьЭлементыПоИмени():


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

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

Элементы = ДокументDOM.ПолучитьЭлементыПоИмени("SKU_CreateUpdateResult");
Если Элементы.Количество() > 0 Тогда
    Значение = Элементы[0].ТекстовоеСодержимое;
    Сообщить("Получено значение: " + Значение);
КонецЕсли;
ЧтениеXML.Закрыть();

Этот способ прост в реализации, однако метод ПолучитьЭлементыПоИмени() ищет элементы "вслепую" по всему дереву без учета иерархии и пространств имен (namespaces). Более надежным и профессиональным подходом является использование языка запросов XPath.

Проанализируем пример использования XPath для быстрого позиционирования на нужном узле:


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

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

// Создаем разыменователь путей для работы с пространствами имен
Разыменователь = ДокументDOM.СоздатьРазыменовательПутейDOM(ДокументDOM);

// Выражение "//" позволяет найти узел в любом месте XML-документа
РезультатXPath = ДокументDOM.ВычислитьВыражениеXPath(
    "//SKU_CreateUpdateResult", 
    ДокументDOM, 
    Разыменователь, 
    ТипРезультатаDOMXPath.ПервыйУпорядоченныйУзел
);

Если РезультатXPath.ОдинарныйУзелРезультата <> Неопределено Тогда
    Значение = РезультатXPath.ОдинарныйУзелРезультата.ТекстовоеСодержимое;
    Сообщить("Значение через XPath: " + Значение);
КонецЕсли;
ЧтениеXML.Закрыть();

Использование XPath гарантирует точечную и быструю выборку данных даже из очень сложных и сильно вложенных XML-пакетов.

---

Метод 2. Использование глобальной Фабрики XDTO

Если XML-ответ представляет собой древовидную структуру, которую необходимо обойти целиком, мы можем воспользоваться глобальным объектом ФабрикаXDTO. Этот метод позволяет прочитать XML в динамический объект XDTO, к свойствам которого можно обращаться "через точку" аналогично свойствам обычной структуры.

Посмотрим на пример реализации чтения XML через фабрику:


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

// Используем стандартную фабрику для чтения произвольного XML
ОбъектXDTO = ФабрикаXDTO.ПрочитатьXML(ЧтениеXML);

// Теперь мы можем обращаться к свойствам объекта по именам тегов XML
Если ОбъектXDTO.Свойства().Получить("Body") <> Неопределено Тогда
    ДанныеОтвета = ОбъектXDTO.Body;
    // Пример обхода вложенных коллекций (если теги повторяются)
    Для Каждого СтрокаПозиции Из ДанныеОтвета.Income_GetFactResponse.Income_Positions.Income_Positions Цикл
        Сообщить("Артикул: " + СтрокаПозиции.SKU + ", Количество: " + СтрокаПозиции.Quantity);
    КонецЦикла;
КонецЕсли;

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

Важное примечание: Чтение произвольного XML без предварительно загруженной XSD-схемы (через дефолтную фабрику) создает динамический тип ОбъектXDTO. При работе с ним будьте внимательны: если какой-то тег в ответе сервера является необязательным и не пришел в конкретном сообщении, обращение к нему "через точку" вызовет ошибку. Перед обращением всегда проверяйте наличие свойства методом ОбъектXDTO.Свойства().Получить("ИмяСвойства"). Для подготовки типизированной модели из сторонней схемы можно использовать обработку для конвертации XSD-схем в модель XDTO.

---

Метод 3. Универсальная рекурсивная функция "XML в Структуру"

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

Правила работы нашего алгоритма:

  1. Простые теги, содержащие только текст, преобразуются в пары "Ключ-Значение" внутри структуры.
  2. Вложенные теги преобразуются во вложенные структуры.
  3. Повторяющиеся одноименные теги (коллекции, строки таблиц) автоматически группируются и помещаются в Массив.

Разберем готовый код рекурсивной функции:


&НаСервереБезКонтекста
Функция ПреобразоватьXMLВКоллекцию(СтрокаXML) Экспорт
    ЧтениеXML = Новый ЧтениеXML;
    ЧтениеXML.УстановитьСтроку(СтрокаXML);
    
    // Перемещаемся к первому элементу
    ЧтениеXML.Прочитать(); 
    
    Результат = ПарситьУзелXML(ЧтениеXML);
    
    ЧтениеXML.Закрыть();
    Возврат Результат;
КонецФункции

&НаСервереБезКонтекста
Функция ПарситьУзелXML(ЧтениеXML)
    СоответствиеТегов = Новый Структура;
    ИмяТекущегоУзла = ЧтениеXML.Имя;
    
    // Если элемент пустой, например <item/>
    Если ЧтениеXML.Пустой() Тогда
        Возврат "";
    КонецЕсли;
    
    Пока ЧтениеXML.Прочитать() Цикл
        ТипУзла = ЧтениеXML.ТипУзла;
        
        Если ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
            ИмяВложенногоУзла = ЧтениеXML.Имя;
            
            // Рекурсивный вызов для обработки вложенных тегов
            ЗначениеВложенногоУзла = ПарситьУзелXML(ЧтениеXML);
            
            // Если свойство уже существует, значит мы встретили массив (повторяющийся тег)
            Если СоответствиеТегов.Свойство(ИмяВложенногоУзла) Тогда
                Если ТипЗнч(СоответствиеТегов[ИмяВложенногоУзла]) <> Тип("Массив") Тогда
                    // Преобразуем существующее одиночное значение в массив
                    ВременныйМассив = Новый Массив;
                    ВременныйМассив.Добавить(СоответствиеТегов[ИмяВложенногоУзла]);
                    СоответствиеТегов[ИмяВложенногоУзла] = ВременныйМассив;
                КонецЕсли;
                СоответствиеТегов[ИмяВложенногоУзла].Добавить(ЗначениеВложенногоУзла);
            Иначе
                СоответствиеТегов.Вставить(ИмяВложенногоУзла, ЗначениеВложенногоУзла);
            КонецЕсли;
            
        ИначеЕсли ТипУзла = ТипУзлаXML.Текст Тогда
            Возврат ЧтениеXML.Значение;
            
        ИначеЕсли ТипУзла = ТипУзлаXML.КонецЭлемента Тогда
            // Завершаем обработку текущего уровня вложенности
            Если СоответствиеТегов.Количество() = 0 Тогда
                Возврат "";
            Иначе
                Возврат СоответствиеТегов;
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;
    
    Возврат СоответствиеТегов;
КонецФункции

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

---

Метод 4. Архитектурно правильный подход: Использование WS-прокси

Если вы интегрируетесь с внешним SOAP-сервисом, ручной разбор XML-пакетов с помощью HTTPСоединение и последующий парсинг строк — это создание избыточного кода ("изобретение велосипеда"). Платформа 1С имеет мощный встроенный механизм для работы с веб-сервисами.

Для правильной настройки интеграции выполним следующие шаги:

  1. Добавим в конфигурацию новую WS-ссылку, указав URL к WSDL-схеме стороннего сервиса (или загрузив WSDL-файл локально). Платформа автоматически импортирует все типы данных и методы сервиса в виде пакетов XDTO.
  2. Будем использовать динамическое создание прокси для вызова методов веб-сервиса напрямую из кода 1С.

Рассмотрим пример кода:


// Создаем определение веб-сервиса на основе сохраненной WS-ссылки (или URL)
Определение = Новый WSОпределения("http://api.server.com/v1/service?wsdl");

// Создаем прокси-объект для выполнения запросов
Прокси = Новый WSProxy(
    Определение, 
    "http://namespaces.server.com/soap", // Пространство имен сервиса
    "SKUService",                        // Имя сервиса
    "SKUServiceSoapPort"                 // Имя порта
);

// Настраиваем параметры авторизации, если это необходимо
Прокси.Пользователь = "API_User";
Прокси.Пароль       = "API_Secret_Password";

// Вызываем метод веб-сервиса напрямую. 
// Платформа сама сериализует параметры, отправит HTTP-запрос и распарсит ответ!
Попытка
    РезультатXDTO = Прокси.SKU_CreateUpdate(ПараметрКод, ПараметрНаименование);
    
    // Читаем возвращаемое значение напрямую как свойство объекта XDTO
    ЗначениеРезультата = РезультатXDTO.SKU_CreateUpdateResult;
    Сообщить("Операция завершена с кодом: " + ЗначениеРезультата);
Исключение
    Сообщить("Ошибка вызова веб-сервиса: " + ОписаниеОшибки());
КонецПопытки;

При использовании WS-прокси вам вообще не придется писать код для чтения XML, обработки тегов и пространств имен. Платформа берет всю рутинную работу по сериализации и десериализации данных на себя, что значительно снижает вероятность ошибок и ускоряет процесс разработки.

---

Резюме и рекомендации

Мы детально проанализировали различные варианты решения задачи преобразования XML в структуры данных. Подведем итоги и определим, какой метод лучше выбрать в зависимости от условий:

← На главную