При формировании текстовых файлов обмена в формате JSON разработчики 1С нередко сталкиваются с ситуацией, когда итоговый документ получается некорректным: на первой итерации отступы и скобки выставляются ожидаемо, а начиная со второй итерации форматирование «плывет», запятые и двоеточия переносятся на отдельные строки, а структура становится невалидной.
Разберем подробнее, почему возникает такое поведение платформы, как устроен внутренний автомат объекта ЗаписьJSON и какими способами можно сформировать красивый и синтаксически верный JSON-документ.
Рассмотрим типичный код, в котором допущена ошибка:
ЗаписьJSON.ЗаписатьНачалоОбъекта();
Пока ВыборкаНоменклатуры.Следующий() Цикл
ЗаписьJSON.ЗаписатьНачалоОбъекта();
ЗаписьJSON.ЗаписатьИмяСвойства("Товар");
ЗаписьJSON.ЗаписатьЗначение(""+ВыборкаНоменклатуры.Ссылка.УникальныйИдентификатор()+"");
ЗаписьJSON.ЗаписатьНачалоМассива();
ЗаписьJSON.ЗаписатьИмяСвойства("ЭтоГруппа");
ЗаписьJSON.ЗаписатьЗначение(ВыборкаНоменклатуры.СсылкаЭтоГруппа);
ЗаписьJSON.ЗаписатьИмяСвойства("Наименование");
ЗаписьJSON.ЗаписатьЗначение(ВыборкаНоменклатуры.Ссылка.Наименование);
ЗаписьJSON.ЗаписатьИмяСвойства("РодительИД");
ЗаписьJSON.ЗаписатьЗначение(""+ВыборкаНоменклатуры.СсылкаРодитель.УникальныйИдентификатор()+"");
ЗаписьJSON.ЗаписатьКонецМассива();
ЗаписьJSON.ЗаписатьКонецОбъекта();
КонецЦикла;
ЗаписьJSON.ЗаписатьКонецОбъекта();
Проанализируем две грубые синтаксические ошибки, которые ломают логику сериализатора:
ЗаписатьНачалоМассива() сериализатор переходит в контекст массива ([]). Массив в JSON представляет собой упорядоченный список значений или объектов, у него нет ключей. Вызов метода ЗаписатьИмяСвойства() внутри массива является грубой ошибкой спецификации JSON.ЗаписатьНачалоОбъекта(), создающий корневой объект ({}). По стандарту JSON объект может содержать только пары «ключ : значение». Запись безымянных вложенных объектов друг за другом внутри родительского объекта не допускается. Корневым контейнером для списка однотипных элементов должен быть массив ([]).Объект ЗаписьJSON в платформе 1С работает как конечный автомат состояний. Когда методы вызываются в недопустимом контексте, внутренний механизм расстановки отступов, запятых и двоеточий рассинхронизируется. Платформа пытается продолжить потоковую запись, что приводит к хаотичному переносу символов на следующих итерациях цикла.
Самый простой, надежный и читаемый способ сформировать JSON в 1С — подготовить данные с помощью встроенных типов Структура и Массив, а затем вызвать глобальную процедуру ЗаписатьJSON(). В этом случае платформа берет на себя строгий контроль синтаксиса, экранирование спецсимволов и форматирование.
Посмотрим на практическую реализацию:
ПараметрыЗаписи = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Авто, Символы.Таб);
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку(ПараметрыЗаписи);
СписокТоваров = Новый Массив;
Пока ВыборкаНоменклатуры.Следующий() Цикл
Товар = Новый Структура;
Товар.Вставить("Товар", Строка(ВыборкаНоменклатуры.Ссылка.УникальныйИдентификатор()));
Товар.Вставить("ЭтоГруппа", ВыборкаНоменклатуры.СсылкаЭтоГруппа);
Товар.Вставить("Наименование", ВыборкаНоменклатуры.Ссылка.Наименование);
Товар.Вставить("РодительИД", Строка(ВыборкаНоменклатуры.СсылкаРодитель.УникальныйИдентификатор()));
СписокТоваров.Добавить(Товар);
КонецЦикла;
ЗаписатьJSON(ЗаписьJSON, СписокТоваров);
Результат = ЗаписьJSON.Закрыть();
Такой подход полностью исключает риск нарушения парности скобок и гарантирует получение валидного JSON-документа.
Если требуется низкоуровневое управление процессом записи без предварительного заполнения коллекций, необходимо строго соблюдать иерархию открывающих и закрывающих методов.
Разберем по шагам правильную структуру:
ЗаписатьНачалоМассива().ЗаписатьНачалоОбъекта().ЗаписатьСвойство(Имя, Значение) вместо связки ЗаписатьИмяСвойства() и ЗаписатьЗначение().ЗаписатьКонецОбъекта().ЗаписатьКонецМассива().Посмотрим на пример правильной потоковой записи:
ПараметрыЗаписи = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Авто, Символы.Таб);
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку(ПараметрыЗаписи);
ЗаписьJSON.ЗаписатьНачалоМассива();
Пока ВыборкаНоменклатуры.Следующий() Цикл
ЗаписьJSON.ЗаписатьНачалоОбъекта();
ЗаписьJSON.ЗаписатьСвойство("Товар", Строка(ВыборкаНоменклатуры.Ссылка.УникальныйИдентификатор()));
ЗаписьJSON.ЗаписатьСвойство("ЭтоГруппа", ВыборкаНоменклатуры.СсылкаЭтоГруппа);
ЗаписьJSON.ЗаписатьСвойство("Наименование", ВыборкаНоменклатуры.Ссылка.Наименование);
ЗаписьJSON.ЗаписатьСвойство("РодительИД", Строка(ВыборкаНоменклатуры.СсылкаРодитель.УникальныйИдентификатор()));
ЗаписьJSON.ЗаписатьКонецОбъекта();
КонецЦикла;
ЗаписьJSON.ЗаписатьКонецМассива();
Результат = ЗаписьJSON.Закрыть();
При выгрузке десятков и сотен тысяч записей формирование единого массива структур в оперативной памяти может привести к чрезмерному потреблению ресурсов сервера. В такой ситуации наилучшим решением будет гибридный подход: корневой контейнер открывается потоково, а каждый отдельный объект сериализуется процедурой ЗаписатьJSON().
Рассмотрим пример реализации:
ПараметрыЗаписи = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Авто, " ");
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку(ПараметрыЗаписи);
ЗаписьJSON.ЗаписатьНачалоМассива();
Пока ВыборкаНоменклатуры.Следующий() Цикл
Элемент = Новый Структура;
Элемент.Вставить("Товар", Строка(ВыборкаНоменклатуры.Ссылка.УникальныйИдентификатор()));
Элемент.Вставить("ЭтоГруппа", ВыборкаНоменклатуры.СсылкаЭтоГруппа);
Элемент.Вставить("Наименование", ВыборкаНоменклатуры.Ссылка.Наименование);
Элемент.Вставить("РодительИД", Строка(ВыборкаНоменклатуры.СсылкаРодитель.УникальныйИдентификатор()));
ЗаписатьJSON(ЗаписьJSON, Элемент);
КонецЦикла;
ЗаписьJSON.ЗаписатьКонецМассива();
Результат = ЗаписьJSON.Закрыть();
Для управления читаемостью итогового файла конструктор объекта ПараметрыЗаписиJSON принимает несколько полезных параметров:
ПереносСтрокJSON.Авто включает форматирование с переносами строк и отступами, а ПереносСтрокJSON.Нет формирует компактную однострочную строку без лишних пробелов (уменьшает объем передаваемых данных).Символы.Таб, два пробела (" ") или четыре пробела (" ").\uXXXX.Использование валидной структуры JSON гарантирует, что файл без ошибок прочитается как сторонними системами, так и средствами платформы 1С через объект ЧтениеJSON.