Почему в 1С возникает Ошибка преобразования данных XDTO при возврате значения с сервера и как ее исправить

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

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

Проанализируем ситуацию и исходный код

Рассмотрим пример кода, который часто становится источником проблемы. Представим функцию, которая должна обработать код номенклатуры на сервере и вернуть его на клиент:


&НаСервере
Функция АналогиНоменклатураПриИзмененииНаСервере(Номенклатура)
    Номенклатура = Номенклатура.Ссылка.ПолучитьОбъект();
    УТН = Номенклатура.Код;
    УТН = СокрЛП(СтрЗаменить(УТН, "00-", ""));
    УТН = СокрЛП(СтрЗаменить(УТН, "ЦБ-", ""));
    Возврат Строка(УТН);
КонецФункции

На первый взгляд, функция должна вернуть строку. Однако при вызове этой функции с клиента выполнение прерывается ошибкой XDTO. Проанализируем, что происходит "под капотом" платформы.

Выясним причину: механизм сериализации и передача параметров

Главная причина кроется не в операторе Возврат, а в том, как 1С передает параметры между клиентом и сервером. В языке 1С параметры в функции по умолчанию передаются по ссылке (аналог ByRef в других языках). Это означает, что если внутри функции вы измените значение переменной-параметра, это изменение должно "вернуться" в ту переменную, которую вы передали при вызове на клиенте.

В нашем примере в строке Номенклатура = Номенклатура.Ссылка.ПолучитьОбъект(); происходит критическая ошибка логики контекстного вызова:

  1. Переменной Номенклатура (которая является входным параметром) присваивается тип СправочникОбъект.
  2. Тип СправочникОбъект существует только на сервере. Его нельзя передать на клиент, так как он не подлежит сериализации в XDTO-пакет, понятный клиентскому приложению.
  3. Когда функция завершает работу, платформа пытается обновить значение исходной переменной на клиенте, упаковывая текущее содержимое Номенклатура в транспортный пакет.
  4. Поскольку там находится "тяжелый" серверный объект, происходит сбой преобразования данных XDTO.

Решение №1: Использование ключевого слова Знач

Самый простой и быстрый способ исправить ошибку — указать платформе, что параметр не нужно возвращать обратно на клиент. Для этого используется ключевое слово Знач (передача по значению). Рассмотрим исправленный вариант:


&НаСервере
Функция АналогиНоменклатураПриИзмененииНаСервере(Знач Номенклатура)
    // Теперь изменения переменной Номенклатура не влияют на клиент
    Номенклатура = Номенклатура.ПолучитьОбъект(); 
    УТН = Номенклатура.Код;
    // ... дальнейшая логика
    Возврат Строка(УТН);
КонецФункции

В этом случае создается локальная копия переменной внутри функции. Даже если мы присвоим ей СправочникОбъект, Запрос или ТаблицуЗначений, платформа не будет пытаться передать эти данные обратно на клиент по завершении функции.

Решение №2: Использование локальных переменных

Хорошим тоном в программировании считается правило: не переопределять входящие параметры. Вместо того чтобы менять значение Номенклатура, заведем отдельную локальную переменную. Это исключит конфликт типов при возврате контекста:


&НаСервере
Функция АналогиНоменклатураПриИзмененииНаСервере(НоменклатураСсылка)
    // Работаем с новой переменной ТекОбъект
    ТекОбъект = НоменклатураСсылка.ПолучитьОбъект();
    УТН = ТекОбъект.Код;
    УТН = СтрЗаменить(УТН, "00-", "");
    Возврат Строка(УТН);
КонецФункции

Оптимизируем код: избавляемся от ПолучитьОбъект()

Разберем еще один важный аспект. Вызов метода ПолучитьОбъект() — это ресурсоемкая операция. Она считывает из базы данных все поля объекта, создает его структуру в памяти сервера и занимает значительный объем ресурсов. Если вам нужно просто получить Код или другой реквизит, делать это через объект крайне неэффективно.

Рассмотрим более правильный подход:

  1. Если у вас уже есть ссылка, обратитесь к реквизиту через точку: Номенклатура.Код. На сервере это допустимо, хотя и вызывает неявный запрос к БД.
  2. Для максимальной производительности используйте функцию ОбщегоНазначения.ЗначенияРеквизитовОбъекта() (если работаете в типовой конфигурации на базе БСП).

Посмотрим на оптимизированный пример:


&НаСервере
Функция ПолучитьОчищенныйКод(НоменклатураСсылка)
    // Прямое обращение к реквизиту без создания тяжелого объекта
    ИсходныйКод = НоменклатураСсылка.Код; 
    
    Результат = СтрЗаменить(ИсходныйКод, "00-", "");
    Результат = СтрЗаменить(Результат, "ЦБ-", "");
    
    Возврат СокрЛП(Результат);
КонецФункции

Нюансы преобразования типов (Число в Строку)

Иногда программисты сталкиваются с тем, что ошибка XDTO возникает при возврате числа. Разберем ситуацию из темы: автор утверждал, что при возврате "999" ошибка есть, а при "ааа" — нет. Это может происходить, если на стороне клиента переменная, принимающая результат, жестко типизирована как Число или связана с реквизитом формы числового типа.

Также помните, что функция Строка(Число) по умолчанию добавляет неразрывные пробелы для разделения разрядов (например, "1 250"). Это может привести к ошибкам, если вы планируете использовать этот код для поиска или сравнения. Чтобы получить "чистую" строку без пробелов, используйте функцию Формат():


// Вместо Строка(КодЧислом)
РезультатСтрокой = Формат(КодЧислом, "ЧГ=0"); 

Что еще нельзя передавать между клиентом и сервером

Чтобы в будущем избежать Ошибки преобразования данных XDTO, проанализируем список типов, которые запрещено возвращать на клиент или передавать в параметры без Знач:

Соблюдение этих простых правил — использование локальных переменных, ключевого слова Знач и отказ от избыточного получения объектов — позволит вам писать стабильный и быстрый код без ошибок преобразования данных.

← На главную