Как исправить ошибку при записи вложенных массивов и объектов в JSON на платформе 1С?

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

Разработка интеграций с веб-сервисами, мобильными приложениями и внешними базами данных — неотъемлемая часть работы современного разработчика 1С. Формат JSON стал стандартом де-факто для обмена данными в веб-среде. Платформа «1С:Предприятие 8» предоставляет мощные встроенные инструменты для генерации таких файлов. Однако при работе с низкоуровневыми объектами потоковой записи начинающие разработчики часто сталкиваются с ошибками валидации структуры. В этой статье мы подробно разберем, почему возникает ошибка при записи вложенных массивов и как правильно выстроить структуру JSON-документа.

Анализ проблемы: почему «ругается» потоковый записьщик 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.

Использование функций преобразования (Callback)

Для того чтобы не выполнять ручное приведение ссылок в коде заполнения структуры, мы можем настроить специальную функцию преобразования типов «на лету». Мы передаем её имя в качестве четвертого параметра функции ЗаписатьJSON():


&НаСервере
Процедура СериализоватьСПреобразованием()
    
    Запись = Новый ЗаписьJSON;
    Запись.УстановитьСтроку();
    
    // Указываем имя экспортной функции, которая будет вызываться для нетипизированных значений
    ЗаписатьJSON(Запись, ГлавныйОбъект, , "ПреобразоватьОбъектыДляJSON");
    
    Результат = Запись.Закрыть();
    
КонецПроцедуры

&НаСервере
Функция ПреобразоватьОбъектыДляJSON(Свойство, Значение, ДополнительныеПараметры, Отказ) Экспорт
    
    // Проверяем, является ли значение ссылкой на справочник или документ
    Если Справочники.ТипВсеСсылки().СодержитТип(ТипЗнч(Значение)) 
        ИЛИ Документы.ТипВсеСсылки().СодержитТип(ТипЗнч(Значение)) Тогда
        Возврат Строка(Значение); // Возвращаем текстовое представление ссылки
    КонецЕсли;
    
    // Для прочих неподдерживаемых типов возвращаем их строковый вид
    Возврат Строка(Значение);
    
КонецФункции

Современные методы работы с JSON (платформа 8.3.23 и выше)

Если ваша информационная база работает на версии платформы 8.3.23 или новее, вам доступны значительно упрощенные методы интеграции, которые не требуют явного создания и закрытия объекта ЗаписьJSON.

Проанализируем новые возможности:

  1. Метод ЗаписатьЗначениеJSON() — принимает массив или структуру 1С и мгновенно возвращает готовую строку в формате JSON.
  2. Метод ПрочитатьЗначениеJSON() — выполняет десериализацию строки JSON напрямую в коллекции 1С (структуры, массивы, соответствия).

Посмотрим на пример использования новых функций:


// Сериализация одной строкой кода
СтрокаJSON = ЗаписатьЗначениеJSON(ГлавныйОбъект);

// Десериализация одной строкой кода
ФинальнаяСтруктура = ПрочитатьЗначениеJSON(СтрокаJSON);

Критически важное преимущество: Новые методы полностью поддерживаются на **Веб-клиенте**. Ранее потоковые методы были доступны исключительно на стороне сервера, что вынуждало разработчиков делать серверные вызовы для базовых операций с JSON на формах.

← На главную