Разработка интеграций с веб-сервисами, мобильными приложениями и внешними базами данных — неотъемлемая часть работы современного разработчика 1С. Формат JSON стал стандартом де-факто для обмена данными в веб-среде. Платформа «1С:Предприятие 8» предоставляет мощные встроенные инструменты для генерации таких файлов. Однако при работе с низкоуровневыми объектами потоковой записи начинающие разработчики часто сталкиваются с ошибками валидации структуры. В этой статье мы подробно разберем, почему возникает ошибка при записи вложенных массивов и как правильно выстроить структуру JSON-документа.
Рассмотрим ситуацию, возникшую в исходном коде автора темы. При выполнении выгрузки платформа генерирует исключение, указывающее на некорректное состояние или неверную структуру JSON. Посмотрим на проблемный фрагмент кода:
ЗаписьJSON.ЗаписатьНачалоОбъекта();
ЗаписьJSON.ЗаписатьИмяСвойства("Реализация");
ЗаписьJSON.ЗаписатьЗначение(СокрЛП(Стр11.Документ));
// Ошибка возникает в этой строке
ЗаписьJSON.ЗаписатьНачалоМассива();
Выясним причину этой ошибки. Объект ЗаписьJSON по умолчанию инициализируется с включенным свойством ПроверятьСтруктуру = Истина. Это означает, что встроенный валидатор платформы жестко следит за соблюдением стандартов JSON и правильной вложенностью элементов. Когда мы вызываем метод ЗаписатьНачалоОбъекта(), открывается фигурная скобка {. Далее мы записываем свойство "Реализация" и его строковое значение. На этом этапе в буфере формируется следующая строка:
{"Реализация": "Реализация товаров и услуг №1 от 15.03.2026"
С точки зрения синтаксиса JSON, внутри объекта за значением свойства должна следовать либо запятая для разделения со следующим свойством (ключом), либо закрывающая фигурная скобка }. Попытка вызвать метод ЗаписатьНачалоМассива() непосредственно после записи значения приводит к синтаксическому тупику, так как массив [ не может быть добавлен внутри объекта без указания имени свойства или без закрытия текущего объекта. Происходит попытка сформировать некорректный JSON вида:
{"Реализация": "Реализация товаров..." [
Платформа фиксирует нарушение правил построения структуры и прерывает выполнение кода с ошибкой.
Для исправления ошибки нам необходимо перестроить логику вызовов методов 1С таким образом, чтобы вложенный массив являлся значением конкретного свойства объекта, либо находился в правильном контейнере.
Проанализируем исправленный рабочий вариант кода, предложенный в обсуждении. Мы последовательно открываем главный объект, объявляем массив для документов-реализаций, а затем внутри цикла для каждого документа формируем правильную вложенную структуру:
ЗаписьJSON.ЗаписатьНачалоОбъекта();
ЗаписьJSON.ЗаписатьИмяСвойства("Реализации"); // Объявляем свойство-контейнер для всех документов
ЗаписьJSON.ЗаписатьНачалоМассива(); // Открываем массив документов
Для Каждого Стр11 ИЗ Объект.ТабДокс Цикл
ЗаписьJSON.ЗаписатьНачалоОбъекта(); // Начинаем новый объект документа в массиве
ЗаписьJSON.ЗаписатьИмяСвойства("Реализация"); // Ключ, значением которого будет вложенный массив
ЗаписьJSON.ЗаписатьНачалоМассива(); // Открываем массив для группировки данных документа
// === Шапка документа ===
ЗаписьJSON.ЗаписатьНачалоОбъекта();
ЗаписьJSON.ЗаписатьИмяСвойства("Документ"); ЗаписьJSON.ЗаписатьЗначение(СокрЛП(Стр11.Документ));
ЗаписьJSON.ЗаписатьИмяСвойства("Номер"); ЗаписьJSON.ЗаписатьЗначение(СокрЛП(Стр11.Документ.Номер));
ЗаписьJSON.ЗаписатьИмяСвойства("Дата"); ЗаписьJSON.ЗаписатьЗначение(СокрЛП(Стр11.Документ.Дата));
ЗаписьJSON.ЗаписатьИмяСвойства("Контрагент"); ЗаписьJSON.ЗаписатьЗначение(СокрЛП(Стр11.Документ.Контрагент));
ЗаписьJSON.ЗаписатьИмяСвойства("ИНН"); ЗаписьJSON.ЗаписатьЗначение(СокрЛП(Стр11.Документ.Контрагент.ИНН));
ЗаписьJSON.ЗаписатьКонецОбъекта();
// === Табличная часть Товары ===
ЗаписьJSON.ЗаписатьНачалоОбъекта();
ЗаписьJSON.ЗаписатьИмяСвойства("Товары");
ЗаписьJSON.ЗаписатьНачалоМассива();
// Здесь может быть цикл обхода строк табличной части Товары
ЗаписьJSON.ЗаписатьКонецМассива();
ЗаписьJSON.ЗаписатьКонецОбъекта();
// === Табличная часть Услуги ===
ЗаписьJSON.ЗаписатьНачалоОбъекта();
ЗаписьJSON.ЗаписатьИмяСвойства("Услуги");
ЗаписьJSON.ЗаписатьНачалоМассива();
// Здесь может быть цикл обхода строк табличной части Услуги
ЗаписьJSON.ЗаписатьКонецМассива();
ЗаписьJSON.ЗаписатьКонецОбъекта();
ЗаписьJSON.ЗаписатьКонецМассива(); // Закрываем массив "Реализация"
ЗаписьJSON.ЗаписатьКонецОбъекта(); // Закрываем объект документа
КонецЦикла;
ЗаписьJSON.ЗаписатьКонецМассива(); // Закрываем массив документов
ЗаписьJSON.ЗаписатьКонецОбъекта(); // Закрываем главный объект
Важно понимать: Теперь перед каждым вызовом метода ЗаписатьНачалоМассива() внутри объекта обязательно вызывается метод ЗаписатьИмяСвойства(). Это гарантирует формирование корректной структуры, где массив выступает в роли значения для соответствующего ключа (свойства) объекта.
Потоковый метод (с ручным вызовом методов начала и конца объектов) незаменим при формировании файлов гигантских объемов, так как он не нагружает оперативную память сервера, записывая данные порциями сразу в файл или текстовый буфер. Однако для небольших и средних объемов данных этот подход избыточно сложен и подвержен ошибкам человеческого фактора.
Гораздо проще и безопаснее использовать сериализацию через встроенные типы коллекций 1С (Структура и Массив) совместно с методом ЗаписатьJSON(). Платформа сделает всю работу по форматированию, расстановке скобок и запятых за вас.
Рассмотрим по шагам, как решить задачу автора темы с помощью коллекций 1С:
// Создаем массив, который будет содержать список всех документов
МассивДокументов = Новый Массив;
Для Каждого СтрокаТаблицы Из Объект.ТабДокс Цикл
// Для каждого документа создаем структуру с реквизитами шапки
СтруктураШапки = Новый Структура;
СтруктураШапки.Вставить("Документ", Строка(СтрокаТаблицы.Документ));
СтруктураШапки.Вставить("Номер", СокрЛП(СтрокаТаблицы.Документ.Номер));
СтруктураШапки.Вставить("Дата", СтрокаТаблицы.Документ.Дата);
СтруктураШапки.Вставить("Контрагент", СокрЛП(СтрокаТаблицы.Документ.Контрагент));
СтруктураШапки.Вставить("ИНН", СокрЛП(СтрокаТаблицы.Документ.Контрагент.ИНН));
// Создаем структуру документа верхнего уровня
СтруктураРеализации = Новый Структура;
// Создаем массив элементов для помещения внутрь свойства "Реализация"
МассивДанныхРеализации = Новый Массив;
МассивДанныхРеализации.Добавить(СтруктураШапки);
// Добавляем табличные части (например, пустые массивы для товаров и услуг)
СтруктураТовары = Новый Структура("Товары", Новый Массив);
СтруктураУслуги = Новый Структура("Услуги", Новый Массив);
МассивДанныхРеализации.Добавить(СтруктураТовары);
МассивДанныхРеализации.Добавить(СтруктураУслуги);
// Наполняем структуру реализации вложенным массивом данных
СтруктураРеализации.Вставить("Реализация", МассивДанныхРеализации);
// Добавляем полученный объект в общий массив
МассивДокументов.Добавить(СтруктураРеализации);
КонецЦикла;
// Формируем финальный объект для сериализации
ГлавныйОбъект = Новый Структура("Реализации", МассивДокументов);
// Выполняем запись в JSON
Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку();
ЗаписатьJSON(Запись, ГлавныйОбъект);
ИтоговыйJSON = Запись.Закрыть();
Подводные камни высокоуровневого метода: Главное ограничение этого способа — несовместимость типов данных. Если в структуру 1С попадет значение типа СправочникСсылка или ДокументСсылка, а также любой другой сложный объект, метод ЗаписатьJSON() завершится аварийно с ошибкой «Передано значение недопустимого типа». Нам нужно предварительно преобразовывать все ссылки в строки с помощью функции Строка() или получать уникальные идентификаторы в формате GUID.
Для того чтобы не выполнять ручное приведение ссылок в коде заполнения структуры, мы можем настроить специальную функцию преобразования типов «на лету». Мы передаем её имя в качестве четвертого параметра функции ЗаписатьJSON():
&НаСервере
Процедура СериализоватьСПреобразованием()
Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку();
// Указываем имя экспортной функции, которая будет вызываться для нетипизированных значений
ЗаписатьJSON(Запись, ГлавныйОбъект, , "ПреобразоватьОбъектыДляJSON");
Результат = Запись.Закрыть();
КонецПроцедуры
&НаСервере
Функция ПреобразоватьОбъектыДляJSON(Свойство, Значение, ДополнительныеПараметры, Отказ) Экспорт
// Проверяем, является ли значение ссылкой на справочник или документ
Если Справочники.ТипВсеСсылки().СодержитТип(ТипЗнч(Значение))
ИЛИ Документы.ТипВсеСсылки().СодержитТип(ТипЗнч(Значение)) Тогда
Возврат Строка(Значение); // Возвращаем текстовое представление ссылки
КонецЕсли;
// Для прочих неподдерживаемых типов возвращаем их строковый вид
Возврат Строка(Значение);
КонецФункции
Если ваша информационная база работает на версии платформы 8.3.23 или новее, вам доступны значительно упрощенные методы интеграции, которые не требуют явного создания и закрытия объекта ЗаписьJSON.
Проанализируем новые возможности:
ЗаписатьЗначениеJSON() — принимает массив или структуру 1С и мгновенно возвращает готовую строку в формате JSON.ПрочитатьЗначениеJSON() — выполняет десериализацию строки JSON напрямую в коллекции 1С (структуры, массивы, соответствия).Посмотрим на пример использования новых функций:
// Сериализация одной строкой кода
СтрокаJSON = ЗаписатьЗначениеJSON(ГлавныйОбъект);
// Десериализация одной строкой кода
ФинальнаяСтруктура = ПрочитатьЗначениеJSON(СтрокаJSON);
Критически важное преимущество: Новые методы полностью поддерживаются на **Веб-клиенте**. Ранее потоковые методы были доступны исключительно на стороне сервера, что вынуждало разработчиков делать серверные вызовы для базовых операций с JSON на формах.