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