Работа с веб-сервисами в 1С через механизм XDTO (XML Data Transfer Objects) часто вызывает затруднения у разработчиков, особенно когда речь идет о передаче сложных структур, содержащих вложенные списки или массивы. Типичная ошибка, с которой сталкиваются программисты: "Значение не может быть установлено свойству, имеющему объектный тип". Эта ошибка возникает в тот момент, когда мы пытаемся присвоить свойству родительского объекта целый массив или список одним махом.
В этой статье мы подробно разберем, почему возникает эта ошибка, как устроена модель данных XDTO и как правильно наполнять вложенные списки параметров веб-сервиса, используя стандартные методы платформы 1С.
Рассмотрим ситуацию, когда в схеме XML (WSDL-описании сервиса) определен тип, содержащий свойство с признаком maxOccurs="unbounded". В объектной модели 1С такое свойство представляется не просто как переменная, а как специальный объект — СписокXDTO. Главная особенность заключается в том, что при создании родительского объекта этот список инициализируется автоматически (хотя и остается пустым).
Разберем типичный ошибочный код, который приводит к сбою:
// ОШИБОЧНЫЙ ПОДХОД
WSПараметр = Сервис.ФабрикаXDTO.Создать(ТипПараметра);
WSСписокНаград = Сервис.ФабрикаXDTO.Создать(ТипСпискаНаград); // Попытка создать отдельный список
Пока Выборка.Следующий() Цикл
НоваяНаграда = Сервис.ФабрикаXDTO.Создать(ТипНаграды);
// ... заполнение свойств награды ...
WSСписокНаград.Добавить(НоваяНаграда);
КонецЦикла;
// В этой строке возникнет ошибка "Значение не может быть установлено свойству..."
WSПараметр.Awards = WSСписокНаград;
Проанализируем причину: свойство Awards уже существует внутри WSПараметр как готовый объект типа СписокXDTO. Платформа 1С не позволяет заменить этот внутренний объект другим объектом, созданным отдельно. Вместо присваивания (=) мы должны обращаться непосредственно к существующему свойству и добавлять элементы прямо в него.
Самый надежный и правильный способ работы с коллекциями XDTO — это последовательное добавление элементов в уже существующее свойство родительского объекта. Давайте перепишем алгоритм правильно. Мы не будем создавать промежуточный объект-массив, а будем работать с коллекцией Awards напрямую.
Рассмотрим по шагам правильную последовательность действий:
ФабрикаXDTO.Создать().Добавить().Посмотрим на пример реализации:
// 1. Создаем корневой объект запроса
WSПараметр = СервисSharepoint.ФабрикаXDTO.Создать(ТипЗапроса);
// 2. Получаем типы данных для создания вложенных элементов
ТипWSAward = СервисSharepoint.ФабрикаXDTO.Тип("http://your-namespace.com", "Award");
// 3. Организуем цикл по данным (например, выборка из БД)
Если Не ВыборкаДетальныеЗаписи.Количество() = 0 Тогда
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
// Создаем объект отдельной награды
WSПараметрAward = СервисSharepoint.ФабрикаXDTO.Создать(ТипWSAward);
WSПараметрAward.Date = ДатаДляSharePoint(ВыборкаДетальныеЗаписи.ДатаПриказа);
WSПараметрAward.Name = XMLСтрока(ВыборкаДетальныеЗаписи.Награда);
// ВАЖНО: Добавляем созданный объект прямо в свойство родителя
// Обратите внимание: мы не используем "=", мы используем метод ".Добавить()"
WSПараметр.Awards.Добавить(WSПараметрAward);
КонецЦикла;
КонецЕсли;
// Теперь объект WSПараметр готов к отправке в веб-сервис
Если структура данных в 1С (например, массив структур) полностью соответствует схеме XDTO, можно воспользоваться объектом СериализаторXDTO. Этот метод позволяет автоматизировать процесс, если вам не хочется вручную перебирать каждое поле в цикле. Однако этот способ требует строгого соответствия имен ключей в структуре 1С именам свойств в XDTO.
Разберем ситуацию: у нас есть массив структур в 1С, и мы хотим быстро превратить его в объект XDTO.
// Предположим, у нас есть МассивНаград, где каждый элемент - Структура
МассивНаград = Новый Массив;
Для Каждого Строка Из ТаблицаДанных Цикл
ДанныеНаграды = Новый Структура("Date, Name", Строка.Дата, Строка.Наименование);
МассивНаград.Добавить(ДанныеНаграды);
КонецЦикла;
// Вместо ручного создания XDTO-объектов, используем сериализатор
// Но помните, что это сработает, если типы в схеме позволяют такую трансформацию
ОбъектXDTO_Awards = СериализаторXDTO.ЗаписатьXDTO(МассивНаград);
Важное замечание: Использование сериализатора удобно для простых преобразований, но при работе с веб-сервисами SharePoint или сложными корпоративными шинами данных (ESB) ручное создание объектов через ФабрикаXDTO считается более "прозрачным" и менее подверженным ошибкам типизации.
Иногда структура XDTO подразумевает наличие "обертки" над списком. Например, свойство Awards — это не сам список, а объект типа ArrayOfAward, у которого внутри есть свойство Award, являющееся списком. В этом случае алгоритм усложняется на один уровень вложенности.
Выясним причину ошибки в таких случаях: если свойство Awards имеет объектный тип (обертка), оно может быть Неопределено при создании родителя. Тогда его нужно создать явно.
Проанализируем пример со вложенной оберткой:
// Создаем родительский объект
WSПараметр = Сервис.ФабрикаXDTO.Создать(ТипWSRequest);
// Если Awards - это сложный тип-обертка (например, ArrayOfAward)
// Сначала создаем саму обертку
ТипОбертки = Сервис.ФабрикаXDTO.Тип("http://namespace", "ArrayOfAward");
ОбъектОбертка = Сервис.ФабрикаXDTO.Создать(ТипОбертки);
// Теперь наполняем список ВНУТРИ обертки
Пока Выборка.Следующий() Цикл
Элемент = Сервис.ФабрикаXDTO.Создать(ТипWSAward);
Элемент.Name = "За заслуги";
// Добавляем в свойство Award, которое является списком внутри обертки
ОбъектОбертка.Award.Добавить(Элемент);
КонецЦикла;
// И только теперь присваиваем ПОЛНОСТЬЮ заполненную обертку родительскому свойству
// Это сработает, так как Awards - это НЕ список, а ОДИНОЧНЫЙ объект-обертка
WSПараметр.Awards = ОбъектОбертка;
Чтобы точно понять, использовать ли .Добавить() или =, воспользуйтесь отладчиком 1С:
WSПараметр.WSПараметр.Awards.СписокXDTO, вы ОБЯЗАНЫ использовать метод Добавить().ОбъектXDTO, вы должны сначала создать этот объект (если он Неопределено), заполнить его свойства и затем присвоить через =.Правильное понимание разницы между списком и объектом в XDTO — ключ к успешной интеграции с любыми внешними системами. Использование метода Добавить() для коллекций является стандартом разработки в среде 1С:Предприятие 8.