Как обработать пустой множественный параметр в веб-сервисе 1С

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

При интеграции сторонних систем (например, сайтов на PHP, Python или мобильных приложений) с веб-сервисами 1С разработчики часто сталкиваются с неочевидным поведением платформы при передаче массивов. Типичный сценарий: веб-сервис принимает объект, содержащий табличную часть или список (множественный параметр XDTO). Нам необходимо четко различать три состояния этого параметра на стороне 1С:

  1. Параметр вообще не передан — это означает, что внешняя система не прислала данные по списку, и существующие данные в базе 1С изменять не нужно.
  2. Параметр передан, но он пустой — это означает, что пользователь на сайте очистил список, и нам необходимо полностью очистить эту информацию в 1С.
  3. Параметр передан и заполнен — стандартный случай, когда мы перезаписываем элементы списка новыми данными.

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

Выясняем причину: особенности работы списков в 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. Программная проверка через метод Установлено()

Это самый красивый и элегантный способ, который позволяет решить проблему исключительно на стороне 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.

Решение 2. Архитектурный паттерн Обертка (Wrapper) в схеме XDTO

Если вы проектируете интеграционное решение с нуля или имеете возможность свободно изменять структуру XSD-схемы, то наилучшим решением с точки зрения мировых стандартов проектирования API является использование объекта-обертки (Wrapper).

Проблема со множественным выбором возникает из-за того, что свойство-список напрямую принадлежит главному объекту. Мы можем изолировать этот список внутри промежуточного единичного объекта.

Давайте проанализируем, как перестроить схему XDTO:

  1. Создадим новый тип объекта XDTO с именем ChildrenListWrapper.
  2. Внутри этого нового типа добавим всего одно свойство — например, Item (или Child) с типом данных ChildrenLine. Настройки для этого внутреннего свойства: Мин. значение = 0, Макс. значение = -1 (это и будет наш реальный список).
  3. В нашем основном объекте (например, DiscountCard) изменим описание свойства ChildrenList. Теперь его тип данных будет не списком, а единичным объектом созданного нами типа ChildrenListWrapper.
  4. Настройки для свойства ChildrenList в основном объекте установим следующие: Мин. значение = 0, Макс. значение = 1, Возможно пустое значение = Истина.

Посмотрим на результат такого изменения. На стороне 1С логика работы со свойством теперь будет выглядеть абсолютно прозрачно и стандартно:


// Проверяем, передан ли контейнер-обертка вообще
Если ДанныеЗапроса.ChildrenList = Неопределено Тогда
    // СОСТОЯНИЕ 1: Тег обертки отсутствует в XML. Данные не трогаем.
    
Иначе
    // Тег обертки присутствует в запросе. 
    // Теперь мы можем безопасно обратиться к списку внутри него.
    ВнутреннийСписок = ДанныеЗапроса.ChildrenList.Item;
    
    Если ВнутреннийСписок.Количество() = 0 Тогда
        // СОСТОЯНИЕ 2: Обертка передана, но список внутри нее пуст. Очищаем данные.
        ОчиститьДанныеВБазе();
    Иначе
        // СОСТОЯНИЕ 3: Список заполнен. Обрабатываем данные.
        Для Каждого СтрокаДетскогоСписка Из ВнутреннийСписок Цикл
            ОбработатьСтрокуСписка(СтрокаДетскогоСписка);
        КонецЦикла;
    КонецЕсли;
КонецЕсли;

Преимущество этого метода заключается в том, что структура данных становится интуитивно понятной для веб-разработчиков (особенно для тех, кто работает с JSON-форматом, где отсутствие ключа и пустой массив внутри объекта — это принципиально разные сущности).

Решение 3. Передача явного флага очистки

Иногда возникают ситуации, когда нет возможности вносить изменения в структуру схемы XDTO (например, схема жестко зафиксирована внешним регламентом), а программный код на стороне 1С хочется сделать максимально простым и защищенным от любых побочных эффектов десериализации.

В этом случае применяется классический прагматичный подход — введение дополнительного управляющего флага. Мы просим разработчиков сайта добавить в отправляемый запрос дополнительный реквизит примитивного типа (например, логического), который будет явно управлять поведением логики 1С.

Давайте добавим в основной объект свойство ClearChildrenList с типом данных boolean (минимальное значение = 0, максимальное = 1). Логика обработки на стороне 1С в этом случае значительно упрощается:


// Сначала анализируем управляющий флаг
Если ДанныеЗапроса.ClearChildrenList = Истина Тогда
    // Сайт явно прислал указание очистить табличную часть
    ОчиститьДанныеОДетяхВБазе(ДанныеЗапроса.Id);
    ЗаписатьВЛог("Данные о детях очищены по запросу сайта.");
    
Иначе
    // Если флаг очистки ложный или не задан, проверяем наличие элементов в списке
    Если ДанныеЗапроса.ChildrenList.Количество() > 0 Тогда
        // Перезаписываем данные новыми значениями из списка
        ОбновитьДанныеСписком(ДанныеЗапроса.Id, ДанныеЗапроса.ChildrenList);
    Иначе
        // Данные не переданы и флаг очистки не поднят - ничего не делаем
        ЗаписатьВЛог("Изменения в список детей не вносились.");
    КонецЕсли;
КонецЕсли;

Этот способ позволяет избежать любых неоднозначностей при парсинге XML/JSON и гарантирует стабильную работу интеграции даже при нестандартном поведении сторонних сериализаторов.

Подводя итог, можно сделать вывод: если вы хотите решить задачу быстро и красиво без изменения контракта веб-сервиса, используйте Решение 1 с проверкой через метод Установлено(). Если вы стремитесь к построению долговечной, масштабируемой и интуитивно понятной архитектуры корпоративного API, вашим выбором должно стать проектирование оберток списков по методологии из Решения 2.

← На главную