При разработке двустороннего обмена данными между мобильным клиентом и серверной базой 1С программисты часто сталкиваются с неочевидным поведением платформы при передаче ссылочных типов данных. Типичная ситуация: документ создается на мобильном устройстве, успешно сериализуется в XML-пакет, но после десериализации на стороне сервера его ссылка оказывается пустой (или преобразуется в битую ссылку без типа). При этом обратный обмен (с сервера на клиент) работает абсолютно корректно.
В этой статье мы подробно разберем, почему возникает эта проблема, проанализируем разницу в поведении базовых коллекций 1С при сериализации и рассмотрим два практических способа решения этой задачи.
Рассмотрим классический сценарий. На стороне мобильного клиента подготавливается пакет данных для отправки на сервер. Для сериализации используется стандартный механизм СериализаторXDTO. Код функций сериализации и десериализации выглядит следующим образом:
Функция Десериализовать(XMLСтруктураСериализованногоОбъекта)
ЧтениеXMLДанных = Новый ЧтениеXML;
ЧтениеXMLДанных.УстановитьСтроку(XMLСтруктураСериализованногоОбъекта);
ТЗ = СериализаторXDTO.ПрочитатьXML(ЧтениеXMLДанных);
ЧтениеXMLДанных.Закрыть();
Возврат ТЗ;
КонецФункции
Функция Сериализовать(ОбъектСериализации)
ДеревоВОбъектеXDTO = СериализаторXDTO.ЗаписатьXDTO(ОбъектСериализации);
МойXML = Новый ЗаписьXML;
МойXML.УстановитьСтроку();
ФабрикаXDTO.ЗаписатьXML(МойXML, ДеревоВОбъектеXDTO);
Возврат МойXML.Закрыть();
КонецФункции
При передаче нового документа, созданного на мобильном клиенте, в XML-пакете присутствует строковое представление его уникального идентификатора (GUID). Однако при десериализации этого XML на сервере свойство Ссылка у десериализованного объекта оказывается пустым или нетипизированным. Из-за этого следующий стандартный код загрузки на сервере дает сбой:
Если ЗначениеЗаполнено(Строка.Ссылка.Номер) Тогда
НовыйОбъект = Строка.Ссылка.ПолучитьОбъект();
Иначе
НовыйОбъект = Документы.WebC_РеализацияАрендаИмущества.СоздатьДокумент();
НовыйОбъект.УстановитьСсылкуНового(Строка.Ссылка);
НовыйОбъект.УстановитьНовыйНомер();
КонецЕсли;
// Заполнение реквизитов...
НовыйОбъект.Записать();
Попытка вызвать метод УстановитьСсылкуНового() завершается ошибкой, так как переданное значение в свойстве Строка.Ссылка не распознается платформой как валидная объектная ссылка нужного типа.
Проанализируем, почему обмен с сервера на клиент проходит успешно, а в обратную сторону ссылка теряется. Дело в типах коллекций данных, которые используются для обмена, и особенностях работы платформы 1С с метаданными:
ПолучитьОбъект(). Поскольку элементы массива являются полноценными объектами базы данных (ДокументОбъект), платформа при сериализации автоматически сопоставляет их с метаданными конфигурации. На стороне мобильного клиента десериализатор четко знает целевой тип данных, поэтому ссылка воссоздается без потерь.Массив, состоящий из объектов типа Структура. Структура и Массив — это универсальные, нетипизированные коллекции. При сериализации структуры свойство, содержащее ссылку на еще не созданный на сервере документ, превращается в обычную строку GUID. При десериализации на сервере платформа не может сопоставить это свойство с конкретным типом метаданных (например, ДокументСсылка.WebC_РеализацияАрендаИмущества) и считывает его как простую строку или объект УникальныйИдентификатор. Соответственно, метод УстановитьСсылкуНового() отказывается принимать некорректный тип данных.Самый простой и элегантный способ сохранить типизацию данных при передаче через XDTO — использовать вместо массива структур объект типа ТаблицаЗначений с явно заданным описанием типов колонок.
Разберем по шагам, как это реализовать:
Пример кода на клиенте:
// Создаем таблицу значений с явным указанием типов
ТаблицаВыгрузки = Новый ТаблицаЗначений;
// Описываем тип для колонки Ссылка
МассивТипов = Новый Массив;
МассивТипов.Добавить(Тип("ДокументСсылка.WebC_РеализацияАрендаИмущества"));
ОписаниеТипаСсылка = Новый ОписаниеТипов(МассивТипов);
ТаблицаВыгрузки.Колонки.Добавить("Ссылка", ОписаниеТипаСсылка);
ТаблицаВыгрузки.Колонки.Добавить("Дата", Новый ОписаниеТипов("Дата"));
// Добавляем остальные необходимые колонки...
// Заполняем таблицу данными
Для Каждого Док Из СписокДокументовНаКлиенте Цикл
НоваяСтрока = ТаблицаВыгрузки.Добавить();
НоваяСтрока.Ссылка = Док.Ссылка;
НоваяСтрока.Дата = Док.Дата;
КонецЦикла;
// Теперь передаем ТаблицуЗначений в сериализатор
XMLДанные = Сериализовать(ТаблицаВыгрузки);
Благодаря тому, что колонка "Ссылка" в ТаблицеЗначений имеет строгий тип ДокументСсылка.WebC_РеализацияАрендаИмущества, СериализаторXDTO сохранит эту информацию в схеме XML. При десериализации на сервере платформа автоматически преобразует строковый GUID в полноценный объект ссылки нужного типа, и метод УстановитьСсылкуНового(Строка.Ссылка) отработает корректно.
Если по каким-то причинам использование ТаблицыЗначений в вашем обмене нежелательно (например, вы хотите использовать легковесные структуры JSON или минимизировать размер XML-пакета), проблему можно решить на стороне сервера. Мы можем принудительно восстановить типизированную ссылку из переданного GUID.
Рассмотрим этот процесс подробнее:
Ссылка передается строковое представление GUID или объект типа УникальныйИдентификатор.УникальныйИдентификатор.ПолучитьСсылку().Посмотрим на пример серверного кода загрузки данных:
// Предположим, Строка.Ссылка пришла как строка GUID или нетипизированный УИД
ИдентификаторСтрокой = Строка.Ссылка;
// Преобразуем значение в УникальныйИдентификатор, если это строка
Если ТипЗнч(ИдентификаторСтрокой) = Тип("Строка") Тогда
УИД = Новый УникальныйИдентификатор(ИдентификаторСтрокой);
Иначе
УИД = ИдентификаторСтрокой;
КонецЕсли;
// Получаем пустую ссылку нужного типа по нашему GUID
НужнаяСсылка = Документы.WebC_РеализацияАрендаИмущества.ПолучитьСсылку(УИД);
// Теперь проверяем наличие объекта в базе данных
НовыйОбъект = Документы.WebC_РеализацияАрендаИмущества.СоздатьДокумент();
НовыйОбъект.УстановитьСсылкуНового(НужнаяСсылка);
// Заполняем реквизиты документа
НовыйОбъект.Дата = Строка.Дата;
НовыйОбъект.Покупатель = Строка.Покупатель;
//...
// Записываем объект в базу данных с жестко заданным GUID
НовыйОбъект.Записать();
Этот способ позволяет отказаться от использования тяжелых схем метаданных ТаблицыЗначений в теле сообщения обмена, сохраняя при этом полный контроль над процессом генерации и сопоставления ссылок на стороне сервера.
Подводя итог, дадим несколько важных рекомендаций, которые помогут избежать проблем с потерей ссылочных типов при интеграции систем на платформе 1С:
Массив, Структура, Соответствие) всегда помните, что типы значений внутри них могут быть приведены к базовым типам (строка, число, булево) в процессе сериализации.ТаблицеЗначений с предварительно описанными типами колонок.ПолучитьСсылку(Новый УникальныйИдентификатор(ИдентификаторСтрокой)).