Работа с JSON
В «1С:Предприятие.Элементе Скрипте» представлены широкие возможности по работе с форматом JSON. Во встроенном языке используются специальные типы для работы с данным форматом, основными из которых являются СериализацияJson, ЧтениеJson и ЗаписьJson. Вы можете автоматически преобразовать в/из JSON большинство типов встроенного языка.
Эти и другие встроенные средства позволяют осуществлять потоковую, объектную или смешанную формы работы с форматом JSON:
- При поток овой работе документ JSON не загружается в память целиком, а считывается по частям. Программа при этом осуществляет навигацию по структуре документа в соответствии с настройками, указанными пользователем. Такой способ удобен при работе с файлами большого размера и/или сложной структурой вложенности объектов.
 - При объектной работе документ JSON загружается в память целиком и считывается в объект, например, соответствие или структуру данных. Такой способ удобен, когда требуется передавать данные как простой и относительно небольшой объект, поэтому объекты JSON используются в основном в API.
 
- При смешанной работе программа осуществляет навигацию по структуре документа потоковым способом, но, когда доходит до требуемого объекта, загружает его объектным способом.
 
Для тех, кто использует JSON версии JSON5 и выше (в которых поддерживаются однострочные комментарии)
Комментарий начинается с символов // и заканчивается с концом строки. Содержимое комментария при чтении JSON игнорируется, то есть результат чтения JSON не включает содержимого комментариев и не содержит какой-либо информации о наличии комментариев.
Потоковый режим работы с JSON
Во встроенном языке для потокового ввода и вывода данных используются соответственно типы ЧтениеJson и ЗаписьJson.
В типе ЧтениеJson существует метод для чтения значения JSON в указанный тип:
ПрочитатьСодержимое(), который использует обобщенный тип данных и принимает как аргумент объект типаНастройкиЧтенияОбъектовJson, как и в примере выше дляСериализацияJson.
Кроме того, существуют методы для чтения значения JSON в один из конкретных типов:
ПрочитатьСодержимоеКакБайты(),ПрочитатьСодержимоеКакБулево(),ПрочитатьСодержимоеКакВремя(),ПрочитатьСодержимоеКакДата(),ПрочитатьСодержимоеКакДатаВремя(),ПрочитатьСодержимоеКакДлительность(),ПрочитатьСодержимоеКакМассив(),ПрочитатьСодержимоеКакМомент(),ПрочитатьСодержимоеКакСоответствие(),ПрочитатьСодержимоеКакСтроку(),ПрочитатьСодержимоеКакУуид(),ПрочитатьСодержимоеКакЧисло().
Объектный режим работы с JSON
Во встроенном языке для объектной работы с данными используются механизмы сериализации и десериализации. Сериализация используется для перевода структуры данных в формат, позволяющий передавать или хранить эти данные, например, в файле. Десериализация — обратная процедура. В «1С:Предприятие.Элементе Скрипте» для этого используется тип СериализацияJson. Методы этого типа позволяют выполнить сериализацию (метод ЗаписатьОбъект() и др.) в поток или строку или десериализацию (метод ПрочитатьОбъект() и др.).
При сериализации порядок разбора значений следующий:
- 
Для 4-х простых базовых типов записывается только значение:
 
При десериализации порядок разбора значений следующий:
- 
Нетипизированное чтение:
Сначала предпринимается попытка разобрать значения 4-х простых базовых типов:
И 2-х сложных:
Если разобрать значение не удалось, то будет выброшено исключение чтения JSON.
 - 
Типизированное чтение:
Сначала предпринимается попытка разобрать значения 4-х простых базовых типов, если указаны они или их базовые типы:
Если разобрать значение не удалось, то будет осуществляться обход всех доступных типов значения. Если в результате обхода значение разобрать удалось, но сразу несколькими типами, то будет выброшено исключение о неявном чтении.
 
Смешанный режим работы с JSON
При смешанной работе с JSON могут использоваться как типы ЧтениеJson и ЗаписьJson, так и тип СериализацияJson. Например:
// Создаем структуру для чтения данных из JSON
структура ФизическоеЛицо
    пер Фамилия: Строка
    пер Имя: Строка
    пер Отчество: Строка
;
// Используем потоковое чтение с помощью типа ЧтениеJson
@НаСервере @ДоступноСКлиента
метод ПрочитатьФизическихЛиц()
    пер ФизическиеЛицаJson =
        "[
            {
                \"Фамилия\": \"Иванов\",
                \"Имя\": \"Иван\",
                \"Отчество\": \"Иванович\"
            },
            {
                \"Фамилия\": \"Петров\",
                \"Имя\": \"Петр\",
                \"Отчество\": \"Петрович\"
            }
        ]"
    пер Чтение = новый ЧтениеJson(ФизическиеЛицаJson) 
    // Позиционируемся на начало массива
    если не Чтение.Следующий()
        возврат
    ;  
    // Позиционируемся на первый элемент массива
    если не Чтение.Следующий()
        возврат
    ;
    // Считываем данные из JSON в структуру для дальнейшего использован ия как объекта
    пока Чтение.ВидУзла != ВидУзлаJson.КонецМассива
        пер ФизЛицо = Чтение.ПрочитатьСодержимое<ФизическоеЛицо>()
        // Записываем полученные данные в базу
    ;
;
Или другой пример:
// Используем потоковое чтение с помощью типа ЧтениеJson
@НаСервере @ДоступноСКлиента
метод ПрочитатьОбращенияПользователей()
     пер ОбращенияJson =
        "[
            {\"Пользователь\":
                {
                    \"Фамилия\": \"Иванов\",
                    \"Имя\": \"Иван\",
                    \"Логин\": \"Иванов\"
                },
                \"Обращения\": [
                    {\"Категория\": \"Жалоба\", \"Текст\": \"текст1\"},
                    {\"Категория\": \"Вопрос\", \"Текст\": \"текст2\"}
                ]
            },
            {\"Пользователь\":
                {
                    \"Фамилия\": \"Петров\",
                    \"Имя\": \"Петр\",
                    \"Логин\": \"Петров\"
                },
                \"Обращения\": [
                    {\"Категория\": \"Благодарность\", \"Текст\": \"текст3\"}
                ]
            }
        ]"
    пер Чтение = новый ЧтениеJson(ОбращенияJson)
    // Позиционируемся на начало массива
    если не Чтение.Следующий()
        возврат
    ;
    // Позиционируемся на первый элемент массива
    если не Чтение.Следующий()
        возврат
    ;
 
    пока Чтение.ВидУзла != ВидУзлаJson.КонецМассива
        // Позиционируемся на свойство Пользователь
        Чтение.Следующий()
        // Позиционируемся на начало объекта Пользователь
        Чтение.Следующий()
        // Считываем данные из JSON в коллекции для дальнейшего использования как объектов
        // Читаем данные пользователя в соответствие
        пер ДанныеПользователя = Чтение.ПрочитатьСодержимоеКакСоответствие()
        // После этого вызова ЧтениеJson будет спозиционировано на свойство Обращения
 
        // Позиционируемся на начало массива
        Чтение.Следующий()
        // Читаем массив обращений
        пер Обращения = Чтение.ПрочитатьСодержимоеКакМассив()
        // После этого вызова ЧтениеJson будет спозиционировано на токен КонецОбъекта
        // Возвращаемся к потоковому чтению JSON
 
        // Переходим к следующему элементу массива
        Чтение.Следующий()
    ;
;
При смешанной работе с JSON в качестве параметров методов ПрочитатьОбъект() и ЗаписатьОбъект() типа СериализацияJson можно использовать экземпляры типов ЧтениеJson и ЗаписьJson. В таком случае при чтении и записи этих объектов будут использоваться те же потоки, что и при их создании.
// Перечисление способов оплаты с указанием JSON-представления
перечисление ТипОплаты
    Наличные,
    @JsonЭлементПеречисления("КредитнаяКарта")
    Карта умолчание
;
// Структура заказа
структура ИнформацияЗаказа
    пер НомерЗаказа: Строка
    пер ТипОплаты: ТипОплаты
    пер Плательщик: Строка
;
// Метод формирования списка ИнформацияЗаказа
метод СоздатьСписокЗаказов(): Массив<ИнформацияЗаказа>
    пер Результат = новый Массив<ИнформацияЗаказа>()
    Результат.Добавить(новый ИнформацияЗаказа("abc-322", ТипОплаты.Карта, "Иванов"))
    Результат.Добавить(новый ИнформацияЗаказа("abc-323", ТипОплаты.Наличные, "Петров"))
    Результат.Добавить(новый ИнформацияЗаказа("abc-324", ТипОплаты.Карта, "Сидоров"))
    возврат Результат
;
// Пример записи JSON с сериализацией
@НаСервере @ДоступноСКлиента
метод ЗаписатьСписокЗаказовВJson()
    // Буфер для записи
    пер Поток = новый СтроковыйПотокЗаписи()
    // Настройки записи объектов в JSON (с валидацией)
    знч Настройки = новый НастройкиЗаписиОбъектовJson()
    
    // Реализация настроек ...
    // Используем СтроковыйПотокЗаписи при создании экземпляра типа ЗаписьJson
    пер Писатель = новый ЗаписьJson(Поток, Настройки)
    
    Писатель.ЗаписатьНачалоОбъекта()
    Писатель.Записать("Описание", "Список заказов")
    Писатель.Записать("МеткаВремени", DateTime{2014-10-06T12:57:35})
    Писатель.ЗаписатьИмяСвойства("Заказы")
    // Используем экземпляр типа Запись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, то используется оно;
 - если в поле структуры указано значение по умолчанию, то используется оно;
 - если указан составной тип, где есть тип 
Неопределено, то используется значениеНеопределено; - если указан тип со значением типа по умолчанию, то испол ьзуется значение по умолчанию;
 - иначе выбрасывается исключение 
ИсключениеЧтенияJson. 
 - 
Если установлено свойство
НастройкиЧтенияОбъектовJson.ИнициализироватьПустыеКоллекции:- если значение поля указано в тексте JSON, то используется оно;
 - если в поле структуры указано значение по умолчанию, то используется оно;
 - если указан составной тип, где есть тип 
Неопределено, то используется значениеНеопределено; - иначе используется значение пустой коллекции типа поля.
 
 
Настройки инициализации применяются в следующем порядке:
ИнициализироватьПустыеКоллекцииИнициализироватьОтсутствующиеПоля
Пример использования псевдонимов свойств
@Локально
структура ТестЗаписиВJson
    пер Строка: Строка
    пер Число: Число
    пер Дата: Дата
    пер Время: Время
    пер ДатаВремя: ДатаВремя
    пер Массив: Массив<Строка>   
    пер Множество: Множество<Число>
    пер Структура: СтруктураАбоненты
    пер Байты: Байты
    пер ТипЗначения: Строка
;
метод ТестСериализацииJSON(): Строка
    пер СтрокаJSON = СериализоватьСтруктуру()
    пер СтрокаИзJSON = ДесериализоватьСтруктуру(СтрокаJSON)
    
    возврат 
        "Результат сериализации: \н\н
        %СтрокаJSON \н\н
        Результат ДЕсериализации: \н\н
        %СтрокаИзJSON \н\н"
;
// Использование псевдонимов свойств
структура СтруктураАбоненты
  пер Абоненты: Множество<СтруктураАбонент>
;
структура СтруктураАбонент
  пер Наименование: Строка
  пер Код: Строка
;
@НаСервере @ДоступноСК лиента
метод СериализоватьСтруктуру(): Строка
    знч АбонентыОбъект = новый СтруктураАбоненты(новый Множество<СтруктураАбонент>())
    АбонентыОбъект.Абоненты.Добавить(новый СтруктураАбон ент("Тестовый абонент", "123456"))
    знч НастройкиЗаписи = новый НастройкиЗаписиОбъектовJson()
    НастройкиЗаписи.ИспользуемыеИменаСвойств.Вставить(Тип<ТестЗаписиВJson>, {"ТипЗначения":"тип"})
    
    знч Объект = новый ТестЗаписиВJson(Строка = "ТестоваяСтрока",
                                       Число = 123,
                                       Дата = Дата.Сейчас(),
                                       Время = Время.Сейчас(),
                                       ДатаВремя = ДатаВремя.Сейчас(),
                                       Массив = ["Тестовый", "Массив", "Строка"],
                                       Множество = {9,8,7,6},
                                       Структура = АбонентыОбъект,
                                       Байты = Байты{0112AAEE FFB2 0011FFAA},
                                       ТипЗначения = "ОписаниеТипаЗначения")
    
    пер ТекстJSON = СериализацияJson.ЗаписатьОбъект(Объект, НастройкиЗаписи)
    
    возврат ТекстJSON    
;
@НаСервере @ДоступноСКлиента
метод ДесериализоватьСтруктуру(СтрокаJSON: Строка): Строка
    
    знч НастройкиЧтения = новый НастройкиЧтенияОбъектовJson()
    НастройкиЧтения.ПсевдонимыСвойств = {
        Тип<ТестЗаписиВJson>: {
        "ТипЗначения": {"тип"}
        }
    }
    
    знч ПрочитанныйОбъект = СериализацияJson.ПрочитатьОбъект<ТестЗаписиВJson>(СтрокаJSON, Тип<ТестЗаписиВJson>, НастройкиЧтения)
    знч ТипОбъекта = ПрочитанныйОбъект.ПолучитьТип().ВСтроку() 
    знч ПредставлениеОбъекта = ПрочитанныйОбъект.ВСтроку()
     
    возврат  "Прочитан объект типа: %ТипОбъекта\н\нСодержимое объекта: \н\н%ПредставлениеОбъекта"  
;
Пример c использованием аннотации свойства структуры
@Локально
структура ТестЗаписиВJson
    пер Строка: Строка
    пер Число: Число
    пер Дата: Дата
    пер Время: Время
    пер ДатаВремя: ДатаВремя
    пер Массив: Массив<Строка>   
    пер Множество: Множество<Число>
    пер Структура: СтруктураАбоненты
    пер Байты: Байты
    
    @JsonСвойство(Имя="тип", Псевдонимы={"тип_значения"})
    пер ТипЗначения: Строка
;
метод ТестСериализацииJSON(): Строка
    пер СтрокаJSON = СериализоватьСтруктуру()
    пер СтрокаИзJSON = ДесериализоватьСтруктуру(СтрокаJSON)
    
    возврат
        "Результат сериализации: \н\н
        %СтрокаJSON \н\н
        Результат ДЕсериализации: \н\н
        %СтрокаИзJSON \н\н"
;
// Использование псевдонимов свойств
структура СтруктураАбоненты
  пер Абоненты: Множество<СтруктураАбонент>
;
структура СтруктураАбонент
  пер Наименование: Строка
  пер Код: Строка
;
@НаСервере @ДоступноСКлиента
метод СериализоватьСтруктуру(): Строка
    знч АбонентыОбъект = новый СтруктураАбоненты(новый Множество<СтруктураАбонент>())
    АбонентыОбъект.Абоненты.Добавить(новый СтруктураАбонент("Тестовый абонент", "123456"))
    
    знч Объект = новый ТестЗаписиВJson(Строка = "ТестоваяСтрока",
                                       Число = 123,
                                       Дата = Дата.Сейчас(),
                                       Время = Время.Сейчас(),
                                       ДатаВремя = ДатаВремя.Сейчас(),
                                       Массив = ["Тестовый", "Массив", "Строка"],
                                       Множество = {9,8,7,6},
                                       Структура = АбонентыОбъект,
                                       Байты = Байты{0112AAEE FFB2 0011FFAA},
                                       ТипЗначения = "ОписаниеТипаЗначения")
    
    пер ТекстJSON = СериализацияJson.ЗаписатьОбъект(Объект)
    
    возврат ТекстJSON    
;
@НаСервере @ДоступноСКлиента
метод ДесериализоватьСтруктуру(СтрокаJSON: Строка): Строка
        
    знч ПрочитанныйОбъект = СериализацияJson.ПрочитатьОбъект<ТестЗаписиВJson>(СтрокаJSON, Тип<ТестЗаписиВJson>)
    знч ТипОбъекта = ПрочитанныйОбъект.ПолучитьТип().ВСтроку() 
    знч ПредставлениеОбъекта = ПрочитанныйОбъект.ВСтроку()
     
    возврат  "Прочитан объект типа: %ТипОбъекта\н\нСодержимое объекта: \н\н%ПредставлениеОбъекта"  
;
Теперь в полученном объекте JSON соответствующее поле будет называться тип:
{
  "Дата" : "2024-01-10",
  "Время" : "19:27:03.857",
  "Массив" : [
    "Тестовый",
    "Массив",
    "Строка"
  ],
  "тип" : "ОписаниеТипаЗначения",
  "Строка" : "ТестоваяСтрока",
  "Структура" : {
    "Абоненты" : [
      {
        "Наименование" : "Тестовый абонент",
        "Код" : "123456"
      }
    ]
  },
  "Байты" : "ARKq7v-yABH_qg==",
  "Множество" : [
    9,
    8,
    7,
    6
  ],
  "Число" : 123,
  "ДатаВремя" : "2024-01-10T19:27:03.857"
}
Передача объекта JSON с псевдонимом тип_значения:
{
  "Дата" : "2024-01-10",
  "Время" : "19:27:03.857",
  "Массив" : [
    "Тестовый",
    "Массив",
    "Строка"
  ],
  "тип_значения" : "ОписаниеТипаЗначения",
  "Строка" : "ТестоваяСтрока",
  "Структура" : {
    "Абоненты" : [
      {
        "Наимен ование" : "Тестовый абонент",
        "Код" : "123456"
      }
    ]
  },
  "Байты" : "ARKq7v-yABH_qg==",
  "Множество" : [
    9,
    8,
    7,
    6
  ],
  "Число" : 123,
  "ДатаВремя" : "2024-01-10T19:27:03.857"
}
Получение объекта с соответствующим псевдониму названием поля:
{Строка=ТестоваяСтрока, Число=123, Дата=2024-01-10, Время=19:27:03.857, ДатаВремя=2024-01-10T19:27:03.857, Массив=[Тестовый, Массив, Строка], Множество={9, 8, 7, 6}, Структура={Абоненты={{Наименование=Тестовый абонент, Код=123456}}}, Байты=0112aaeeffb20011ffaa, ТипЗначения=ОписаниеТипаЗначения}
Пример инициализации отсутствующих полей
структура Структура
    пер Поле: Строка
    пер Поле2: Строка = ""
    пер Поле3: Строка?
    пер Поле4: Строка? = ""
    пер Поле5: Строка|Число?
;
@НаСервере @ДоступноСКлиента
метод ЧтениеИзJson()
    знч JSON = "{}"
    знч Настройки = новый НастройкиЧтенияОбъектовJson()
    Настройки.ИнициализироватьОтсутствующиеПоля = Истина
    знч Результат = СериализацияJson.ПрочитатьОбъект<Структура>(JSON, Тип<Структура>, Настройки)
    //  Результат  = {Поле=, Поле2=, Поле3=Неопределено, Поле4=, Поле5=Н еопределено}
;
структура СтруктураСКоллекциями
    пер Поле: Массив<Строка>?
    пер Поле2: Массив<Строка>
;
@НаСервере @ДоступноСКлиента
метод ЧтениеСтруктурыСКоллекциями()
    знч JSON = "{}"
    знч Настройки = новый НастройкиЧтенияОбъектовJson()
    Настройки.ИнициализироватьПустыеКоллекции = Истина
    пер Результат = СериализацияJson.ПрочитатьОбъект<СтруктураСКоллекциями>(JSON, Тип<СтруктураСКоллекциями>, Настройки)
    // Результат = {Поле=Неопределено, Поле2=[]}
;