При интеграции сторонних систем (например, сайтов на PHP, Python или мобильных приложений) с веб-сервисами 1С разработчики часто сталкиваются с неочевидным поведением платформы при передаче массивов. Типичный сценарий: веб-сервис принимает объект, содержащий табличную часть или список (множественный параметр XDTO). Нам необходимо четко различать три состояния этого параметра на стороне 1С:
Проблема заключается в том, что платформа 1С при десериализации XML-пакета со множественным свойством (где максимальное количество элементов равно -1 или больше 1) автоматически инициализирует его как пустой СписокXDTO. В итоге попытка проверить тип свойства или получить его значение напрямую всегда возвращает пустой список, стирая разницу между первым и вторым состояниями. Давайте подробно разберем, почему так происходит, и рассмотрим три надежных способа решения этой проблемы.
Для начала проанализируем поведение платформы. Когда мы настраиваем схему XDTO и указываем для множественного свойства параметры Мин. значение = 0, Макс. значение = -1 и Возможно пустое значение = Истина, мы ожидаем, что при отсутствии тега в XML-запросе платформа вернет нам Неопределено.
Однако в архитектуре 1С заложено следующее правило: любое списковое свойство объекта XDTO всегда инициализируется. Это сделано для удобства разработчиков, чтобы обращение к свойству-списку «через точку» в цикле Для каждого не вызывало аварийного завершения работы программы, если список оказался пуст.
Что касается настройки Возможно пустое значение (в терминах XML-схем — свойство nillable="true") для списков, то она работает на уровне элементов самого списка, а не на уровне свойства-контейнера. Если внешняя система присылает XML-запрос с конструкцией вида:
<upd:ChildrenList xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></upd:ChildrenList>
то 1С интерпретирует это не как «список равен Null», а как «в списке содержится один элемент, значение которого равно Неопределено». В итоге в коде 1С мы получаем заполненный СписокXDTO с единственным элементом, имеющим пустое значение. Это нарушает логику обработки и усложняет чтение данных.
Рассмотрим по шагам три практических метода преодоления этого ограничения.
Это самый красивый и элегантный способ, который позволяет решить проблему исключительно на стороне 1С без изменения существующей схемы XDTO и без доработок на стороне внешнего сайта.
Несмотря на то, что прямое обращение к свойству-списку всегда возвращает инициализированный пустой СписокXDTO, платформа хранит информацию о том, присутствовал ли физически данный тег в исходном XML-пакете. Получить эту информацию мы можем с помощью встроенного метода Установлено() (или его англоязычного аналога IsSet()) объекта XDTO.
Давайте разберем пример реализации логики обработки в коде веб-сервиса 1С:
// Предположим, что в веб-сервис пришел объект "ДанныеЗапроса" типа "ОбъектXDTO"
// Нам нужно обработать множественное свойство "ChildrenList"
Если НЕ ДанныеЗапроса.Установлено("ChildrenList") Тогда
// СОСТОЯНИЕ 1: Параметр вообще не передавался в XML.
// Никаких действий с базой данных производить не нужно.
ЗаписатьВЛог("Параметр ChildrenList отсутствует в запросе. Данные не изменяем.");
ИначеЕсли ДанныеЗапроса.ChildrenList.Количество() = 0 Тогда
// СОСТОЯНИЕ 2: Тег был передан, но он пустой (например, <upd:ChildrenList/>)
// Это явное указание на то, что нужно очистить данные в базе 1С.
ОчиститьДанныеОДетяхВБазе(ДанныеЗапроса.Id);
ЗаписатьВЛог("Параметр ChildrenList передан пустым. Данные в базе очищены.");
Иначе
// СОСТОЯНИЕ 3: Список передан и содержит элементы для обработки.
ОчиститьДанныеОДетяхВБазе(ДанныеЗапроса.Id); // Перед заполнением очищаем старые записи
Для Каждого СтрокаСписка Из ДанныеЗапроса.ChildrenList Цикл
// Проверяем элемент на корректность (исключаем ошибочный nil-элемент)
Если СтрокаСписка = Неопределено Тогда
Продолжить;
КонецЕсли;
ДобавитьСведенияОРебенке(ДанныеЗапроса.Id, СтрокаСписка.Name, СтрокаСписка.DateOfBirth);
КонецЦикла;
ЗаписатьВЛог("Данные о детях успешно обновлены.");
КонецЕсли;
Использование метода Установлено() позволяет гибко разграничить логику приложения, полностью сохраняя совместимость с текущей спецификацией API.
Если вы проектируете интеграционное решение с нуля или имеете возможность свободно изменять структуру XSD-схемы, то наилучшим решением с точки зрения мировых стандартов проектирования API является использование объекта-обертки (Wrapper).
Проблема со множественным выбором возникает из-за того, что свойство-список напрямую принадлежит главному объекту. Мы можем изолировать этот список внутри промежуточного единичного объекта.
Давайте проанализируем, как перестроить схему XDTO:
XDTO с именем ChildrenListWrapper.Item (или Child) с типом данных ChildrenLine. Настройки для этого внутреннего свойства: Мин. значение = 0, Макс. значение = -1 (это и будет наш реальный список).DiscountCard) изменим описание свойства ChildrenList. Теперь его тип данных будет не списком, а единичным объектом созданного нами типа ChildrenListWrapper.ChildrenList в основном объекте установим следующие: Мин. значение = 0, Макс. значение = 1, Возможно пустое значение = Истина.Посмотрим на результат такого изменения. На стороне 1С логика работы со свойством теперь будет выглядеть абсолютно прозрачно и стандартно:
// Проверяем, передан ли контейнер-обертка вообще
Если ДанныеЗапроса.ChildrenList = Неопределено Тогда
// СОСТОЯНИЕ 1: Тег обертки отсутствует в XML. Данные не трогаем.
Иначе
// Тег обертки присутствует в запросе.
// Теперь мы можем безопасно обратиться к списку внутри него.
ВнутреннийСписок = ДанныеЗапроса.ChildrenList.Item;
Если ВнутреннийСписок.Количество() = 0 Тогда
// СОСТОЯНИЕ 2: Обертка передана, но список внутри нее пуст. Очищаем данные.
ОчиститьДанныеВБазе();
Иначе
// СОСТОЯНИЕ 3: Список заполнен. Обрабатываем данные.
Для Каждого СтрокаДетскогоСписка Из ВнутреннийСписок Цикл
ОбработатьСтрокуСписка(СтрокаДетскогоСписка);
КонецЦикла;
КонецЕсли;
КонецЕсли;
Преимущество этого метода заключается в том, что структура данных становится интуитивно понятной для веб-разработчиков (особенно для тех, кто работает с JSON-форматом, где отсутствие ключа и пустой массив внутри объекта — это принципиально разные сущности).
Иногда возникают ситуации, когда нет возможности вносить изменения в структуру схемы XDTO (например, схема жестко зафиксирована внешним регламентом), а программный код на стороне 1С хочется сделать максимально простым и защищенным от любых побочных эффектов десериализации.
В этом случае применяется классический прагматичный подход — введение дополнительного управляющего флага. Мы просим разработчиков сайта добавить в отправляемый запрос дополнительный реквизит примитивного типа (например, логического), который будет явно управлять поведением логики 1С.
Давайте добавим в основной объект свойство ClearChildrenList с типом данных boolean (минимальное значение = 0, максимальное = 1). Логика обработки на стороне 1С в этом случае значительно упрощается:
// Сначала анализируем управляющий флаг
Если ДанныеЗапроса.ClearChildrenList = Истина Тогда
// Сайт явно прислал указание очистить табличную часть
ОчиститьДанныеОДетяхВБазе(ДанныеЗапроса.Id);
ЗаписатьВЛог("Данные о детях очищены по запросу сайта.");
Иначе
// Если флаг очистки ложный или не задан, проверяем наличие элементов в списке
Если ДанныеЗапроса.ChildrenList.Количество() > 0 Тогда
// Перезаписываем данные новыми значениями из списка
ОбновитьДанныеСписком(ДанныеЗапроса.Id, ДанныеЗапроса.ChildrenList);
Иначе
// Данные не переданы и флаг очистки не поднят - ничего не делаем
ЗаписатьВЛог("Изменения в список детей не вносились.");
КонецЕсли;
КонецЕсли;
Этот способ позволяет избежать любых неоднозначностей при парсинге XML/JSON и гарантирует стабильную работу интеграции даже при нестандартном поведении сторонних сериализаторов.
Подводя итог, можно сделать вывод: если вы хотите решить задачу быстро и красиво без изменения контракта веб-сервиса, используйте Решение 1 с проверкой через метод Установлено(). Если вы стремитесь к построению долговечной, масштабируемой и интуитивно понятной архитектуры корпоративного API, вашим выбором должно стать проектирование оберток списков по методологии из Решения 2.