Почему сбивается форматирование и структура при записи JSON в 1С 8.3?

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

При формировании текстовых файлов обмена в формате JSON разработчики 1С нередко сталкиваются с ситуацией, когда итоговый документ получается некорректным: на первой итерации отступы и скобки выставляются ожидаемо, а начиная со второй итерации форматирование «плывет», запятые и двоеточия переносятся на отдельные строки, а структура становится невалидной.

Разберем подробнее, почему возникает такое поведение платформы, как устроен внутренний автомат объекта ЗаписьJSON и какими способами можно сформировать красивый и синтаксически верный JSON-документ.

Анализ проблемы: почему нарушается форматирование

Рассмотрим типичный код, в котором допущена ошибка:


ЗаписьJSON.ЗаписатьНачалоОбъекта();
Пока ВыборкаНоменклатуры.Следующий() Цикл
    ЗаписьJSON.ЗаписатьНачалоОбъекта();
    ЗаписьJSON.ЗаписатьИмяСвойства("Товар");
    ЗаписьJSON.ЗаписатьЗначение(""+ВыборкаНоменклатуры.Ссылка.УникальныйИдентификатор()+"");
    ЗаписьJSON.ЗаписатьНачалоМассива();
    
    ЗаписьJSON.ЗаписатьИмяСвойства("ЭтоГруппа");
    ЗаписьJSON.ЗаписатьЗначение(ВыборкаНоменклатуры.СсылкаЭтоГруппа); 
    
    ЗаписьJSON.ЗаписатьИмяСвойства("Наименование");
    ЗаписьJSON.ЗаписатьЗначение(ВыборкаНоменклатуры.Ссылка.Наименование); 
    
    ЗаписьJSON.ЗаписатьИмяСвойства("РодительИД");
    ЗаписьJSON.ЗаписатьЗначение(""+ВыборкаНоменклатуры.СсылкаРодитель.УникальныйИдентификатор()+""); 
    
    ЗаписьJSON.ЗаписатьКонецМассива();
    ЗаписьJSON.ЗаписатьКонецОбъекта();
КонецЦикла;
ЗаписьJSON.ЗаписатьКонецОбъекта();

Проанализируем две грубые синтаксические ошибки, которые ломают логику сериализатора:

  1. Именованные свойства внутри массива. После вызова метода ЗаписатьНачалоМассива() сериализатор переходит в контекст массива ([]). Массив в JSON представляет собой упорядоченный список значений или объектов, у него нет ключей. Вызов метода ЗаписатьИмяСвойства() внутри массива является грубой ошибкой спецификации JSON.
  2. Безымянные объекты внутри родительского объекта. В самом начале вызывается метод ЗаписатьНачалоОбъекта(), создающий корневой объект ({}). По стандарту JSON объект может содержать только пары «ключ : значение». Запись безымянных вложенных объектов друг за другом внутри родительского объекта не допускается. Корневым контейнером для списка однотипных элементов должен быть массив ([]).

Объект ЗаписьJSON в платформе 1С работает как конечный автомат состояний. Когда методы вызываются в недопустимом контексте, внутренний механизм расстановки отступов, запятых и двоеточий рассинхронизируется. Платформа пытается продолжить потоковую запись, что приводит к хаотичному переносу символов на следующих итерациях цикла.

Решение 1: Сериализация готовых коллекций 1С (рекомендуемый способ)

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

Посмотрим на практическую реализацию:


ПараметрыЗаписи = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Авто, Символы.Таб);
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку(ПараметрыЗаписи);

СписокТоваров = Новый Массив;

Пока ВыборкаНоменклатуры.Следующий() Цикл
    Товар = Новый Структура;
    Товар.Вставить("Товар", Строка(ВыборкаНоменклатуры.Ссылка.УникальныйИдентификатор()));
    Товар.Вставить("ЭтоГруппа", ВыборкаНоменклатуры.СсылкаЭтоГруппа);
    Товар.Вставить("Наименование", ВыборкаНоменклатуры.Ссылка.Наименование);
    Товар.Вставить("РодительИД", Строка(ВыборкаНоменклатуры.СсылкаРодитель.УникальныйИдентификатор()));
    
    СписокТоваров.Добавить(Товар);
КонецЦикла;

ЗаписатьJSON(ЗаписьJSON, СписокТоваров);
Результат = ЗаписьJSON.Закрыть();

Такой подход полностью исключает риск нарушения парности скобок и гарантирует получение валидного JSON-документа.

Решение 2: Корректная ручная потоковая запись

Если требуется низкоуровневое управление процессом записи без предварительного заполнения коллекций, необходимо строго соблюдать иерархию открывающих и закрывающих методов.

Разберем по шагам правильную структуру:

  1. Открываем массив как корневой элемент через ЗаписатьНачалоМассива().
  2. В цикле открываем объект элемента через ЗаписатьНачалоОбъекта().
  3. Последовательно пишем свойства объекта парами «имя – значение». Для сокращения кода рекомендуется использовать метод ЗаписатьСвойство(Имя, Значение) вместо связки ЗаписатьИмяСвойства() и ЗаписатьЗначение().
  4. Закрываем объект элемента через ЗаписатьКонецОбъекта().
  5. После завершения цикла закрываем корневой массив через ЗаписатьКонецМассива().

Посмотрим на пример правильной потоковой записи:


ПараметрыЗаписи = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Авто, Символы.Таб);
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку(ПараметрыЗаписи);

ЗаписьJSON.ЗаписатьНачалоМассива();

Пока ВыборкаНоменклатуры.Следующий() Цикл
    ЗаписьJSON.ЗаписатьНачалоОбъекта();
    
    ЗаписьJSON.ЗаписатьСвойство("Товар", Строка(ВыборкаНоменклатуры.Ссылка.УникальныйИдентификатор()));
    ЗаписьJSON.ЗаписатьСвойство("ЭтоГруппа", ВыборкаНоменклатуры.СсылкаЭтоГруппа);
    ЗаписьJSON.ЗаписатьСвойство("Наименование", ВыборкаНоменклатуры.Ссылка.Наименование);
    ЗаписьJSON.ЗаписатьСвойство("РодительИД", Строка(ВыборкаНоменклатуры.СсылкаРодитель.УникальныйИдентификатор()));
    
    ЗаписьJSON.ЗаписатьКонецОбъекта();
КонецЦикла;

ЗаписьJSON.ЗаписатьКонецМассива();
Результат = ЗаписьJSON.Закрыть();

Решение 3: Гибридный метод для больших массивов данных

При выгрузке десятков и сотен тысяч записей формирование единого массива структур в оперативной памяти может привести к чрезмерному потреблению ресурсов сервера. В такой ситуации наилучшим решением будет гибридный подход: корневой контейнер открывается потоково, а каждый отдельный объект сериализуется процедурой ЗаписатьJSON().

Рассмотрим пример реализации:


ПараметрыЗаписи = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Авто, "  ");
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку(ПараметрыЗаписи);

ЗаписьJSON.ЗаписатьНачалоМассива();

Пока ВыборкаНоменклатуры.Следующий() Цикл
    Элемент = Новый Структура;
    Элемент.Вставить("Товар", Строка(ВыборкаНоменклатуры.Ссылка.УникальныйИдентификатор()));
    Элемент.Вставить("ЭтоГруппа", ВыборкаНоменклатуры.СсылкаЭтоГруппа);
    Элемент.Вставить("Наименование", ВыборкаНоменклатуры.Ссылка.Наименование);
    Элемент.Вставить("РодительИД", Строка(ВыборкаНоменклатуры.СсылкаРодитель.УникальныйИдентификатор()));
    
    ЗаписатьJSON(ЗаписьJSON, Элемент);
КонецЦикла;

ЗаписьJSON.ЗаписатьКонецМассива();
Результат = ЗаписьJSON.Закрыть();

Параметры форматирования в объекте ПараметрыЗаписиJSON

Для управления читаемостью итогового файла конструктор объекта ПараметрыЗаписиJSON принимает несколько полезных параметров:

  1. ПереносСтрокJSON — системное перечисление. Значение ПереносСтрокJSON.Авто включает форматирование с переносами строк и отступами, а ПереносСтрокJSON.Нет формирует компактную однострочную строку без лишних пробелов (уменьшает объем передаваемых данных).
  2. Отступ — строка, определяющая символ отступа для каждого уровня вложенности. Чаще всего используются Символы.Таб, два пробела (" ") или четыре пробела (" ").
  3. ЭкранированиеСимволов — перечисление, управляющее преобразованием национальных символов (например, кириллицы) в последовательности \uXXXX.

Использование валидной структуры JSON гарантирует, что файл без ошибок прочитается как сторонними системами, так и средствами платформы 1С через объект ЧтениеJSON.

← На главную