При разработке механизмов обмена данными в среде 1С:Предприятие программисты часто сталкиваются с ситуацией, когда код, успешно работающий в интерактивном режиме (при запуске пользователем), внезапно «падает» с ошибкой при выполнении в фоновом задании. Одной из наиболее коварных ошибок является Ошибка при вызове метода контекста (ЗаписатьJSON): Передано значение недопустимого типа. Разберем детально, почему это происходит и как правильно подготовить данные для сериализации.
Метод ЗаписатьJSON является «низкоуровневым» инструментом платформы. Он спроектирован для работы исключительно с примитивными типами данных и базовыми коллекциями. В идеальном сценарии в структуру, которую вы передаете для записи, должны входить только:
NULLНеопределено (при определенных настройках)Проблема заключается в том, что в фоновом задании контекст выполнения более строгий. Если в структуру (например, при формировании адреса) попадает объект типа СправочникСсылка, ПеречислениеСсылка или УникальныйИдентификатор, метод ЗаписатьJSON не сможет автоматически преобразовать их в строку и выдаст ошибку. В обычном клиентском сеансе, особенно при отладке, некоторые типы могут неявно приводиться к строковому представлению или обрабатываться платформой иначе из-за особенностей серверного вызова с клиента. В «чистом» фоновом задании такая вольность исключена. Для локализации подобных проблем в процессе разработки удобно использовать способ отладки длительных операций БСП в текущем сеансе.
Рассмотрим ситуацию, описанную в теме: типовая функция РаботаСАдресами.БГУ_СтруктураАдресаВСтруктуруJSON возвращает структуру, которая внешне кажется «чистой», но внутри может содержать ссылки на виды контактной информации или классификаторы. Именно они становятся «миной замедленного действия» для сериализатора.
Если вы работаете в конфигурации, основанной на БСП (Библиотека стандартных подсистем), или в типовых решениях (БП 3.0, БГУ 2.0, ERP), самым надежным способом будет использование функций, которые специально предназначены для нормализации структур перед их записью в JSON. Рассмотрим пример использования функции из модуля УправлениеКонтактнойИнформациейСлужебный.
Вместо прямого вызова ЗаписатьJSON, проанализируем и применим следующий подход:
// Вместо этого кода, который падает:
// ЗаписатьJSON(ЗаписьJSON, СтруктураАдреса);
// Используем типовую очистку (пример для БП/БГУ):
СтрокаJSON = УправлениеКонтактнойИнформациейСлужебный.СтруктураВСтрокуJSON(СтруктураАдреса);
// Функция внутри обходит структуру рекурсивно и заменяет ссылки на их строковые представления
Этот метод хорош тем, что он «знает» особенности структур адресов и корректно обрабатывает вложенные коллекции, преобразуя все специфические типы 1С в примитивы, понятные формату JSON.
Если в вашей конфигурации нет подходящей функции очистки или структура данных слишком сложная, рекомендуется использовать возможности СериализаторXDTO. В отличие от базового метода ЗаписатьJSON, сериализатор XDTO умеет автоматически обрабатывать ссылки 1С, превращая их в строковые UUID, и корректно форматировать даты.
Разберем, как изменить код для использования XDTO:
ЗаписьJSON = Новый ЗаписьJSON;
ПараметрыЗаписиJSON = Новый ПараметрыЗаписиJSON();
ЗаписьJSON.УстановитьСтроку(ПараметрыЗаписиJSON);
// Вместо прямого метода ЗаписатьJSON используем СериализаторXDTO
СериализаторXDTO.ЗаписатьJSON(ЗаписьJSON, СтруктураАдреса, НазначениеТипаXML.Явное);
РезультатСтрокой = ЗаписьJSON.Закрыть();
Важный нюанс: при использовании СериализаторXDTO.ЗаписатьJSON платформа берет на себя всю работу по обходу дерева значений и приведению типов. Это исключает ошибку «Недопустимого типа» практически в 100% случаев.
В ситуациях, когда вы ограничены в использовании типовых модулей или XDTO, мы можем реализовать рекурсивную функцию «очистки» структуры. Проанализируем пример такой функции, которую можно разместить в общем модуле:
Функция СтерилизоватьОбъектДляJSON(Значение) Экспорт
ТипЗнч = ТипЗнч(Значение);
Если ТипЗнч = Тип("Структура") Тогда
Результат = Новый Структура;
Для Каждого КлючИЗначение Из Значение Цикл
Результат.Вставить(КлючИЗначение.Ключ, СтерилизоватьОбъектДляJSON(КлючИЗначение.Значение));
КонецЦикла;
Возврат Результат;
ИначеЕсли ТипЗнч = Тип("Массив") Тогда
Результат = Новый Массив;
Для Каждого Элемент Из Значение Цикл
Результат.Добавить(СтерилизоватьОбъектДляJSON(Элемент));
КонецЦикла;
Возврат Результат;
ИначеЕсли Справочники.ТипВсеСсылки().СодержитТип(ТипЗнч)
Или Перечисления.ТипВсеСсылки().СодержитТип(ТипЗнч) Тогда
// Преобразуем ссылку в строку или UUID
Возврат Строка(Значение.УникальныйИдентификатор());
ИначеЕсли ТипЗнч = Тип("Дата") Тогда
Возврат Формат(Значение, "ДФ=yyyy-MM-ddTHH:mm:ss");
Иначе
// Для примитивных типов возвращаем как есть
Возврат Значение;
КонецЕсли;
КонецФункции
Используя такую функцию перед вызовом ЗаписатьJSON, вы гарантируете, что в поток данных не попадут сложные объекты 1С, вызывающие сбой в фоновом задании. В качестве альтернативы стандартной сериализации можно использовать способ передачи произвольных параметров между сеансами и фоновыми заданиями.
Основная сложность, озвученная в обсуждении, — невозможность быстро поймать ошибку отладчиком в фоновом задании, особенно если код вызывается через оператор Выполнить(). Рассмотрим эффективные методы диагностики:
ЗаписатьXML. Метод ЗаписатьXML более лоялен к типам 1С, и полученный файл позволит вам увидеть, какие именно поля содержат ссылки вместо строк.ЗаписьЖурналаРегистрации("ОтладкаJSON", УровеньЖурналаРегистрации.Ошибка,,, ЗначениеВСтрокуВнутр(стАдрес));
ЗначениеИзСтрокиВнутр.
Выполнить(), попробуйте временно заменить его прямым вызовом процедуры для локализации места сбоя.Подведем итог: ошибка ЗаписатьJSON в фоне — это всегда сигнал о том, что в ваших данных присутствует «непримитивный» тип. Использование СериализаторXDTO или проверенных функций из УправлениеКонтактнойИнформациейСлужебный — самый быстрый и правильный путь решения проблемы.