← Назад к вопросам

Какие универсальные коллекции нельзя передавать между клиентом и сервером?

2.2 Middle🔥 121 комментариев
#Стандарты разработки

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Универсальные коллекции: ограничения при передаче между клиентом и сервером

Это классический вопрос на собеседованиях и в практике работы с архитектурой клиент-сервер в 1С. Рассмотрю все коллекции, которые передавать нельзя, почему и как обойти ограничения.

Краткий ответ

Нельзя передавать следующие коллекции:

  1. Соответствие (Map)
  2. Список (List) — только в некоторых случаях
  3. Дерево значений (Tree)

Можно передавать:

  1. Массив (Array) — всегда
  2. ТаблицаЗначений (Table) — всегда
  3. Структура (Structure) — всегда

Почему эти ограничения существуют

В архитектуре 1С клиент и сервер работают на разных машинах и используют сериализацию данных при передаче. Не все типы коллекций сериализуются одинаково.

Проблема 1: Соответствие (Map)

Нельзя передать от сервера к клиенту (и наоборот):

// ❌ ЭТО ВЫЗОВЕТ ОШИБКУ
Функция ПолучитьСоответствие() Экспорт
    
    ДанныеСоответствия = Новое Соответствие;
    ДанныеСоответствия.Вставить("Ключ1", "Значение1");
    ДанныеСоответствия.Вставить("Ключ2", "Значение2");
    
    Возврат ДанныеСоответствия; // Ошибка при передаче!
    
КонецФункции

Почему: Соответствие сохраняет порядок и использует хеширование, что не сериализуется.

Решение — преобразовать в Структуру:

// ✅ ПРАВИЛЬНО
Функция ПолучитьДанные() Экспорт
    
    // Используем Структуру вместо Соответствия
    ДанныеСтруктура = Новая Структура;
    ДанныеСтруктура.Вставить("Ключ1", "Значение1");
    ДанныеСтруктура.Вставить("Ключ2", "Значение2");
    
    Возврат ДанныеСтруктура; // OK!
    
КонецФункции

Или преобразовать в Массив пар:

// ✅ АЛЬТЕРНАТИВА
Функция ПолучитьДанныеКакМассив() Экспорт
    
    ДанныеСоответствия = Новое Соответствие;
    ДанныеСоответствия.Вставить("Ключ1", "Значение1");
    ДанныеСоответствия.Вставить("Ключ2", "Значение2");
    
    // Преобразуем в массив структур [ключ, значение]
    МассивДанных = Новый Массив;
    Для Каждого Пара Из ДанныеСоответствия Цикл
        НоваяПара = Новая Структура("Ключ,Значение", Пара.Ключ, Пара.Значение);
        МассивДанных.Добавить(НоваяПара);
    КонецЦикла;
    
    Возврат МассивДанных; // OK!
    
КонецФункции

Проблема 2: Список (List)

Ограничено — можно передать только ПУСТОЙ Список:

// ❌ ОШИБКА — Список с данными
Функция ПолучитьСписок() Экспорт
    
    МойСписок = Новый Список;
    МойСписок.Добавить("Элемент1");
    МойСписок.Добавить("Элемент2");
    
    Возврат МойСписок; // Ошибка при передаче!
    
КонецФункции

// ✅ ОК — пустой Список
Функция ПолучитьПустойСписок() Экспорт
    МойСписок = Новый Список; // Без элементов
    Возврат МойСписок; // OK!
КонецФункции

Решение — использовать Массив:

// ✅ ПРАВИЛЬНО
Функция ПолучитьДанные() Экспорт
    
    МойМассив = Новый Массив;
    МойМассив.Добавить("Элемент1");
    МойМассив.Добавить("Элемент2");
    
    Возврат МойМассив; // OK!
    
КонецФункции

Проблема 3: Дерево значений (Tree)

Нельзя передать напрямую:

// ❌ ОШИБКА
Функция ПолучитьДерево() Экспорт
    
    МойСтруктураДерева = Новое ДеревоЗначений;
    МойСтруктураДерева.Колонки.Добавить("Номер");
    МойСтруктураДерева.Колонки.Добавить("Наименование");
    
    СтрокаДерева = МойСтруктураДерева.Строки.Добавить();
    СтрокаДерева.Номер = "1";
    
    Возврат МойСтруктураДерева; // Ошибка при передаче!
    
КонецФункции

Решение 1 — преобразовать в ТаблицуЗначений:

// ✅ ВАРИАНТ 1: Как плоская таблица
Функция ПолучитьДеревоКакТабличку() Экспорт
    
    ТабДанных = Новая ТаблицаЗначений;
    ТабДанных.Колонки.Добавить("Уровень", Новый ОписаниеТипов("Число"));
    ТабДанных.Колонки.Добавить("Номер", Новый ОписаниеТипов("Строка"));
    ТабДанных.Колонки.Добавить("Наименование", Новый ОписаниеТипов("Строка"));
    ТабДанных.Колонки.Добавить("РодительНомер", Новый ОписаниеТипов("Строка"));
    
    // Заполняем как иерархию
    СтрокаРодитель = ТабДанных.Добавить();
    СтрокаРодитель.Уровень = 0;
    СтрокаРодитель.Номер = "1";
    СтрокаРодитель.Наименование = "Родитель";
    
    СтрокаДочка = ТабДанных.Добавить();
    СтрокаДочка.Уровень = 1;
    СтрокаДочка.Номер = "1.1";
    СтрокаДочка.Наименование = "Дочка";
    СтрокаДочка.РодительНомер = "1";
    
    Возврат ТабДанных; // OK!
    
КонецФункции

Решение 2 — сериализация в JSON:

// ✅ ВАРИАНТ 2: Через JSON
Функция ПолучитьДеревоКакJSON() Экспорт
    
    МойСтруктураДерева = Новое ДеревоЗначений;
    МойСтруктураДерева.Колонки.Добавить("Номер");
    МойСтруктураДерева.Колонки.Добавить("Наименование");
    
    // Заполняем дерево
    СтрокаДерева = МойСтруктураДерева.Строки.Добавить();
    СтрокаДерева.Номер = "1";
    СтрокаДерева.Наименование = "Корень";
    
    // Сериализуем
    JSONДерева = ДеревоВJSON(МойСтруктураДерева);
    Возврат JSONДерева; // Возвращаем строку JSON
    
КонецФункции

Функция ДеревоВJSON(Дерево)
    МассивДанных = ДеревоВМассив(Дерево.Строки);
    Возврат ПреобразоватьВJSON(МассивДанных);
КонецФункции

Функция ДеревоВМассив(СтрокиДерева)
    МассивСтрок = Новый Массив;
    
    Для Каждого Строка Из СтрокиДерева Цикл
        ДанныеСтроки = Новая Структура();
        ДанныеСтроки.Вставить("Номер", Строка.Номер);
        ДанныеСтроки.Вставить("Наименование", Строка.Наименование);
        
        // Рекурсивно добавляем потомков
        Если Строка.Строки.Количество() > 0 Тогда
            ДанныеСтроки.Вставить("Потомки", ДеревоВМассив(Строка.Строки));
        КонецЕсли;
        
        МассивСтрок.Добавить(ДанныеСтроки);
    КонецЦикла;
    
    Возврат МассивСтрок;
КонецФункции

Таблица совместимости

Тип коллекцииМожно передатьПримечания
Массив✅ ДаВсегда работает
Структура✅ ДаВсегда работает
ТаблицаЗначений✅ ДаОтличный выбор
Соответствие❌ НетИспользовать Структуру
Список⚠️ Только пустойИспользовать Массив
ДеревоЗначений❌ НетИспользовать ТаблицаЗначений + иерархия
ФиксированныеДанные✅ ДаИммутабельная обёртка

Практические примеры

Сценарий: нужно вернуть результаты поиска со сложной структурой

// ❌ НЕПРАВИЛЬНО
Функция ПолучитьРезультатыПоиска(ПоисковыйТекст) Экспорт
    Результаты = Новое Соответствие; // ОШИБКА!
    // ...
    Возврат Результаты;
КонецФункции

// ✅ ПРАВИЛЬНО
Функция ПолучитьРезультатыПоиска(ПоисковыйТекст) Экспорт
    
    ТабЛтатов = Новая ТаблицаЗначений;
    ТабЛтатов.Колонки.Добавить("ID", Новый ОписаниеТипов("Строка"));
    ТабЛтатов.Колонки.Добавить("Наименование", Новый ОписаниеТипов("Строка"));
    ТабЛтатов.Колонки.Добавить("Рейтинг", Новый ОписаниеТипов("Число"));
    
    // Заполняем результаты
    Для Каждого Товар Из НайтиТовары(ПоисковыйТекст) Цикл
        НоваяСтрока = ТабЛтатов.Добавить();
        НоваяСтрока.ID = Товар.Ссылка.УникальныйИдентификатор();
        НоваяСтрока.Наименование = Товар.Наименование;
        НоваяСтрока.Рейтинг = Товар.Рейтинг;
    КонецЦикла;
    
    Возврат ТабЛтатов; // OK!
    
КонецФункции

Общие принципы

Правило 1: Передавай по сети только простые типы

  • Массивы простых типов ✅
  • Таблицы значений ✅
  • Структуры с простыми полями ✅
  • Всё остальное ❌

Правило 2: Для сложных структур используй ТаблицаЗначений

  • Легко сериализуется
  • Легко десериализуется
  • Быстрая передача
  • Понятная структура

Правило 3: Если нужна иерархия, добавь поле уровня/родителя

ТабДанных.Колонки.Добавить("Уровень");
ТабДанных.Колонки.Добавить("РодительИД");

Правило 4: Для очень сложных структур используй JSON

  • Универсальный формат
  • Поддерживается везде
  • Легко отладить

В итоге, это важное ограничение архитектуры, которое разработчик должен помнить при работе с клиент-серверными приложениями в 1С. Знание этих нюансов спасает от многих ошибок в production.