При разработке интеграций через веб-сервисы (SOAP) в среде 1С:Предприятие программисты часто сталкиваются с неочевидным поведением объектов XDTO. Типичная ситуация: вы создаете структуру данных на основе XDTO-пакета, заполняете ее из таблицы значений или выборки запроса с помощью стандартного метода ЗаполнитьЗначенияСвойств(), проверяете объект в отладчике — он заполнен. Однако на стороне системы-приемника или даже при анализе XML-пакета выясняется, что поля приходят пустыми. При этом переход на «ручное» присваивание каждого поля через точку (явный маппинг) мгновенно решает проблему.
В этой статье мы подробно разберем, почему универсальный механизм 1С дает сбой в контексте XDTO, проанализируем внутреннее устройство сериализации и выработаем надежные стратегии заполнения данных для веб-сервисов.
Главное отличие объекта XDTO от обычной структуры или прикладного объекта 1С заключается в том, что каждое его свойство обладает не только значением, но и специальным внутренним состоянием — признаком «Установлено». Механизм XML-сериализации 1С, который формирует SOAP-ответ, ориентируется именно на этот флаг.
Проанализируем ситуацию: когда мы выполняем прямое присваивание вида ОбъектXDTO.Поле = Значение, платформа гарантированно переключает состояние свойства в «Установлено». Если же используется метод ЗаполнитьЗначенияСвойств(), в некоторых версиях платформы или при определенных настройках XSD-схемы (например, при использовании типов, отличных от стандартных строк xs:string), данные записываются в память объекта, но флаг для сериализатора не взводится. В итоге при формировании XML-тега система считает, что значение не было задано, и либо игнорирует узел, либо передает его пустым.
XDTO-пакеты очень чувствительны к типам данных. Даже если в пакете указан тип string, это может быть не примитивный xs:string, а производный тип с ограничениями (фасетами). Рассмотрим основные нюансы:
ЗаполнитьЗначенияСвойств() работает как простое копирование. Если в колонке Таблицы Значений (ТЗ) лежит число, а XDTO ожидает строку, метод может не выполнить преобразование так, как этого ожидает схема XSD.nillable="true", пустое значение из 1С может превратиться в xsi:nil="true", что приемник интерпретирует как NULL, а не как пустую строку. Явное присвоение "" помогает 1С четко понять намерение разработчика.Часто ошибка кроется в последовательности действий при работе со списками объектов XDTO. Посмотрим на пример кода, который может работать нестабильно:
ТаблицаЗначенийТип = ФабрикаXDTO.Тип("http://mysite.ru", "ValueTable");
СтрокаТЗТип = ФабрикаXDTO.Тип("http://mysite.ru", "TableString");
ВыхТаблица = ФабрикаXDTO.Создать(ТаблицаЗначенийТип);
Для каждого Стр из ТзДляВыгрузки Цикл
// ОШИБКА: Создание объекта в отрыве от родительского списка
СтрокаXDTO = ФабрикаXDTO.Создать(СтрокаТЗТип);
ЗаполнитьЗначенияСвойств(СтрокаXDTO, Стр);
ВыхТаблица.Composition.Добавить(СтрокаXDTO);
КонецЦикла;
Выясним, как сделать это правильно. Для стабильной сериализации рекомендуется сначала добавлять пустой элемент в список, получать на него ссылку и уже затем заполнять свойства этого «живого» объекта, который уже находится в контексте родителя:
Для каждого Стр из ТзДляВыгрузки Цикл
// ПРАВИЛЬНО: Добавляем элемент и получаем ссылку
НоваяСтрокаXDTO = ВыхТаблица.Composition.Добавить();
// Теперь заполняем объект, который уже является частью коллекции
ЗаполнитьЗначенияСвойств(НоваяСтрокаXDTO, Стр);
// Если ЗаполнитьЗначенияСвойств все же не срабатывает, используем явный маппинг:
// НоваяСтрокаXDTO.Поле1 = Стр.Поле1;
КонецЦикла;
Хотя явное присваивание кажется более трудоемким, для веб-сервисов это считается лучшей практикой. Проанализируем преимущества этого метода:
IsSet.Строка(), Формат() или преобразовать дату в формат XML (ISO 8601).ЗаполнитьЗначенияСвойств().Пример надежного маппинга:
Для каждого Стр из ТзДляВыгрузки Цикл
НоваяСтрока = ВыхТаблица.Composition.Добавить();
НоваяСтрока.ID = Строка(Стр.ID);
НоваяСтрока.Name = СокрЛП(Стр.Имя);
НоваяСтрока.Amount = Стр.Сумма;
КонецЦикла;
Если полей в таблице очень много и они постоянно меняются, можно реализовать обход свойств программно, используя метод Установить(). Это позволит обойти ограничения ЗаполнитьЗначенияСвойств(), сохраняя при этом универсальность.
Разберем пример такой процедуры:
Процедура ЗаполнитьXDTOИзСтруктуры(ОбъектXDTO, Данные)
ТипОбъекта = ОбъектXDTO.Тип();
Для каждого Свойство Из ТипОбъекта.Свойства Цикл
ИмяПоля = Свойство.Имя;
Если Данные.Свойства().Найти(ИмяПоля) <> Неопределено Тогда
// Используем метод Установить(), который принудительно взводит флаг IsSet
ОбъектXDTO.Установить(ИмяПоля, Данные[ИмяПоля]);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Эта процедура проходит по описанию свойств самого XDTO-пакета и ищет соответствующие данные в источнике (например, в строке ТЗ). Использование метода Установить() является ключевым моментом, так как он работает на более низком уровне взаимодействия с фабрикой XDTO.
Означает ли вышесказанное, что ЗаполнитьЗначенияСвойств() вообще нельзя использовать? Нет. Посмотрим, в каких случаях этот метод будет работать корректно:
Структура) или соответствий.Однако в интеграционных задачах, где важна точность каждого байта в XML-пакете, лучше придерживаться стратегии явного определения данных. Это избавит вас от трудноуловимых багов, когда данные «вроде бы есть» в отладке, но «почему-то нет» в сети.
Мы проанализировали проблему отсутствия данных при передаче через веб-сервис и пришли к следующим выводам:
ЗаполнитьЗначенияСвойств() может не активировать внутренний механизм пометки свойства XDTO как заполненного (флаг IsSet).Добавить(), а затем заполнять его свойства.Установить() объекта XDTO в цикле по свойствам типа.Соблюдение этих правил позволит создавать стабильные и производительные интеграционные решения на платформе 1С, работающие корректно с любыми внешними системами.