При интеграции 1С с внешними веб-сервисами (будь то REST API или SOAP) разработчики регулярно сталкиваются с необходимостью обработки входящих XML-пакетов. Часто требуется преобразовать полученный XML-документ в привычную структуру 1С или быстро извлечь из него конкретные значения, например, идентификаторы, статусы выполнения операций или списки номенклатуры.
В этой статье мы подробно проанализируем, почему стандартные инструменты сериализации 1С могут выдавать ошибки при работе со сторонними XML-данными, и детально разберем четыре практических метода преобразования XML в структуру или объектные модели 1С.
Типичная ошибка начинающих разработчиков — попытка прочитать произвольный 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-документами необходимо применять другие механизмы.
---
Если вам не нужно преобразовывать весь 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-пакетов.
---
Если 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.
---
Для разработчиков, которые предпочитают работать исключительно со стандартными коллекциями 1С (такими как Структура и Массив), наиболее удобным решением станет написание универсального парсера. Такой парсер рекурсивно обходит весь XML и преобразует его в структуру.
Правила работы нашего алгоритма:
Массив.Разберем готовый код рекурсивной функции:
&НаСервереБезКонтекста
Функция Преобразовать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С.
---
Если вы интегрируетесь с внешним SOAP-сервисом, ручной разбор XML-пакетов с помощью HTTPСоединение и последующий парсинг строк — это создание избыточного кода ("изобретение велосипеда"). Платформа 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 в структуры данных. Подведем итоги и определим, какой метод лучше выбрать в зависимости от условий: