При реализации механизмов обмена данными через HTTP-сервисы разработчики часто сталкиваются с ситуацией, когда небольшие порции данных (например, 10–20 позиций номенклатуры) выгружаются без проблем, но при попытке выгрузить весь справочник система выдает ошибку «Передано значение недопустимого типа». На первый взгляд может показаться, что мы упираемся в ограничение по длине строки или объему данных, однако в 1С:Предприятии 8 механизмы работы с JSON практически не имеют программных ограничений по длине. Истинная причина всегда скрыта в составе передаваемых данных.
В этой статье мы подробно разберем, почему возникает эта ошибка, как правильно настроить сериализацию и какие методы позволяют передавать огромные объемы данных без сбоев.
Для начала проанализируем текст ошибки, который возникает в методе ЗаписатьJSON. Если в стеке вызовов мы видим «Передано значение недопустимого типа», это означает, что в структуру или массив, которые вы передаете для записи, попал объект, тип которого 1С не умеет преобразовывать в JSON автоматически.
Стандартный метод ЗаписатьJSON «из коробки» поддерживает только следующие типы:
Если в ваши данные попала СправочникСсылка, ДокументСсылка, ПеречислениеСсылка или, что случается крайне часто, тип Неопределено (который отличается от NULL), метод ЗаписатьJSON прервет работу с ошибкой. Тот факт, что 10 элементов выгружаются нормально, а 2000 — нет, лишь подтверждает, что где-то в основной массе данных затесался «битый» реквизит или незаполненное значение.
Рассмотрим первый и самый эффективный рубеж защиты — подготовку данных. Если вы формируете структуру для JSON на основании выборки из запроса, обязательно используйте функцию ЕСТЬNULL. Часто программисты забывают, что левые соединения в запросах могут возвращать NULL, который 1С понимает, но при определенных манипуляциях данные могут превратиться в Неопределено или некорректные ссылки.
Проанализируем пример «безопасного» запроса:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЕСТЬNULL(Номенклатура.Наименование, """") КАК Наименование,
| ЕСТЬNULL(Номенклатура.Артикул, """") КАК Артикул,
| ЕСТЬNULL(Номенклатура.Цена, 0) КАК Цена
|ИЗ
| Справочник.Номенклатура КАК Номенклатура";
Использование таких конструкций гарантирует, что в результирующую структуру попадут только примитивные типы (строки и числа), которые гарантированно записываются в JSON.
Разберем ситуацию, когда в названиях товаров присутствуют спецсимволы: кавычки, обратные слэши или невидимые символы ASCII. Чтобы 1С корректно обрабатывала такие строки, необходимо инициализировать объект ПараметрыЗаписиJSON.
Посмотрим на пример настройки параметров, который помогает избежать проблем с экранированием:
Функция ПреобразоватьДанныеВJSON(ДанныеДляОтвета)
// Настраиваем параметры записи
// ЭкранированиеСимволовJSON.СимволыВнеASCII позволяет корректно передавать спецсимволы
ПараметрыЗаписи = Новый ПараметрыЗаписиJSON(
, // Отступ (не нужен для компактности)
, // Символ отступа
, // ЭкранироватьСлэш (Ложь по умолчанию)
ЭкранированиеСимволовJSON.СимволыВнеASCII,
Истина, // ИспользоватьДвойныеКавычки
Истина // ЭкранироватьОдинарныеКавычки
);
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку(ПараметрыЗаписи);
Попытка
ЗаписатьJSON(ЗаписьJSON, ДанныеДляОтвета);
Исключение
// Если ошибка произошла здесь, значит в ДанныеДляОтвета есть недопустимый тип
ЗаписьJSON.Закрыть();
ВызватьИсключение "Ошибка сериализации: " + ОписаниеОшибки();
КонецПопытки;
Возврат ЗаписьJSON.Закрыть();
КонецФункции
Если вам все же необходимо передавать ссылки или объекты, которые 1С не сериализует автоматически, следует использовать четвертый параметр метода ЗаписатьJSON — ИмяФункцииПреобразования. Это позволит нам «на лету» превращать ссылки в их строковые УИДы.
Рассмотрим, как это реализовать:
// В модуле, где происходит запись
ЗаписатьJSON(ЗаписьJSON, Данные, , "МояФункцияПреобразования", ЭтотОбъект);
// Сама функция преобразования
Функция МояФункцияПреобразования(Свойство, Значение, ДополнительныеПараметры, Отказ) Экспорт
// Если встречаем ссылку, преобразуем её в строку УИД
Если Справочники.ТипВсеСсылки().СодержитТип(ТипЗнч(Значение)) Тогда
Возврат Строка(Значение.УникальныйИдентификатор());
ИначеЕсли Значение = Неопределено Тогда
Возврат ""; // Заменяем Неопределено на пустую строку
КонецЕсли;
// Для остальных типов оставляем стандартную обработку
Отказ = Истина;
Возврат Неопределено;
КонецФункции
Если ваша задача — выгрузить справочник на 100 000 позиций, использование ЗаписьJSON.УстановитьСтроку() приведет к ошибке «Недостаточно памяти», так как 1С попытается собрать всю гигантскую строку в оперативной памяти сервера. В этом случае нам нужно перейти от работы со строками к работе с файлами или потоками.
Разберем алгоритм потоковой записи:
ЗаписьJSON.ОткрытьФайл(). Это позволит сбрасывать данные на диск порциями.ЗаписатьJSON, используем ручное формирование структуры.Проанализируем пример кода для потоковой выгрузки:
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.ОткрытьФайл(ПутьКФайлу);
ЗаписьJSON.ЗаписатьНачалоОбъекта();
ЗаписьJSON.ЗаписатьИмяСвойства("items");
ЗаписьJSON.ЗаписатьНачалоМассива();
Выборка = Справочники.Номенклатура.Выбрать();
Пока Выборка.Следующий() Цикл
// Записываем каждый элемент по отдельности
СтруктураЭлемента = Новый Структура("id, name",
Строка(Выборка.Ссылка.УникальныйИдентификатор()),
Выборка.Наименование
);
ЗаписатьJSON(ЗаписьJSON, СтруктураЭлемента);
КонецЦикла;
ЗаписьJSON.ЗаписатьКонецМассива();
ЗаписьJSON.ЗаписатьКонецОбъекта();
ЗаписьJSON.Закрыть();
Этот подход позволяет обходить любые ограничения на длину JSON, так как потребление памяти будет стабильно низким вне зависимости от количества записей в справочнике.
Если вы все еще получаете ошибку типа на большом объеме данных, воспользуйтесь следующими советами:
ЗаписатьJSON, внимательно изучите переменную со значениями. Ищите поля, где вместо ожидаемой строки стоит Неопределено или объект ссылки.Таким образом, мы выяснили, что ограничений на длину JSON в 1С практически не существует. Главное — следить за типами данных в коллекции и использовать потоковую запись для больших объемов информации.