При работе с внешними сервисами мы часто сталкиваемся с необходимостью обрабатывать ответы в формате JSON. JSON — это гибкий и широко используемый формат обмена данными, который позволяет передавать как простые значения, так и сложные вложенные структуры, включая объекты и массивы. В 1С для работы с JSON предусмотрены специальные объекты и функции, которые мы подробно рассмотрим. Часто возникает вопрос, как правильно получить доступ к данным, когда они находятся внутри вложенных объектов или массивов, и как затем преобразовать их в удобный для 1С табличный вид.
Давайте разберем типичную ситуацию, когда сервер возвращает ответ с глубокой вложенностью, и выясним, как эффективно извлечь нужные данные и подготовить их для заполнения табличного поля.
Для начала давайте рассмотрим основной инструмент для чтения JSON в 1С — функцию ПрочитатьJSON. Она позволяет загрузить весь JSON-додокумент в память и преобразовать его в стандартные объекты 1С: Структура, Соответствие или Массив.
ЧтениеJSON: Это первый шаг для инициализации процесса чтения.УстановитьСтроку.ПрочитатьJSON: Эта функция непосредственно парсит строку и возвращает результат в виде объекта 1С.ЧтениеJSON: После завершения работы с JSON-строкой, важно закрыть объект ЧтениеJSON.Рассмотрим пример JSON, который мы получаем от сервера:
{"dt":1616144400,"sunrise":1616125630,"sunset":1616169215,"temp":{"day":265.11,"min":258.36,"max":265.5,"night":258.88,"eve":264.41,"morn":263.17},"feels_like":{"day":257.85,"night":254.04,"eve":258.59,"morn":256.64},"pressure":1019,"humidity":93,"dew_point":263.7,"wind_speed":6.12,"wind_deg":45,"weather":[{"id":600,"main":"Snow","description":"light snow","icon":"13d"}],"clouds":100,"pop":0.98,"snow":3.71,"uvi":1.14}
Наш основной интерес в данном случае — это раздел "temp", который является вложенным JSON-объектом, а также раздел "weather", который представляет собой массив объектов.
После вызова функции ПрочитатьJSON, весь JSON-додокумент будет представлен в 1С как объект Структура или Соответствие. Если корневой элемент JSON — это объект (заключенный в фигурные скобки {}), то результатом будет Структура или Соответствие. Если корневой элемент — массив (заключенный в квадратные скобки []), то результатом будет Массив.
В нашем случае, корневой элемент — объект, поэтому ПрочитатьJSON вернет Структуру. Доступ к вложенным данным осуществляется через свойства этой структуры.
Давайте попробуем получить данные из раздела "temp":
// Предположим, СтрокаОтвета содержит JSON-строку
СтрокаОтвета = "{\"dt\":1616144400,\"sunrise\":1616125630,\"sunset\":1616169215,\"temp\":{\"day\":265.11,\"min\":258.36,\"max\":265.5,\"night\":258.88,\"eve\":264.41,\"morn\":263.17},\"feels_like\":{\"day\":257.85,\"night\":254.04,\"eve\":258.59,\"morn\":256.64},\"pressure\":1019,\"humidity\":93,\"dew_point\":263.7,\"wind_speed\":6.12,\"wind_deg\":45,\"weather\":[{\"id\":600,\"main\":\"Snow\",\"description\":\"light snow\",\"icon\":\"13d\"}],\"clouds\":100,\"pop\":0.98,\"snow\":3.71,\"uvi\":1.14}";
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(СтрокаОтвета);
// ПрочитатьJSON вернет Структуру (или Соответствие), так как корневой элемент - объект
СтруктураОтвета = ПрочитатьJSON(ЧтениеJSON, Истина); // Истина - для использования Соответствия вместо Структуры, если имена свойств могут быть невалидными для имен реквизитов
// Закрываем объект чтения JSON
ЧтениеJSON.Закрыть();
// Теперь СтруктураОтвета содержит все данные. Доступ к "temp"
// СтруктураОтвета.temp - это тоже Структура (или Соответствие)
Если ТипЗнч(СтруктураОтвета.temp) = Тип("Структура") Тогда
// Получаем вложенную структуру temp
ВложеннаяСтруктураТемператур = СтруктураОтвета.temp;
Сообщить("Температура днем: " + ВложеннаяСтруктураТемператур.day);
Сообщить("Минимальная температура: " + ВложеннаяСтруктураТемператур.min);
Сообщить("Максимальная температура: " + ВложеннаяСтруктураТемператур.max);
// Если мы хотим обойти все свойства вложенной структуры, используем цикл "Для Каждого ... Из ..."
Для Каждого КлючИЗначение Из ВложеннаяСтруктураТемператур Цикл
Сообщить("Ключ: " + КлючИЗначение.Ключ + ", Значение: " + Строка(КлючИЗначение.Значение));
КонецЦикла;
ИначеЕсли ТипЗнч(СтруктураОтвета.temp) = Тип("Соответствие") Тогда
// Если ПрочитатьJSON было вызвано с Истина и имена свойств JSON не являются корректными
// именами реквизитов 1С, то ПрочитатьJSON вернет Соответствие.
// Для Соответствия также можно использовать цикл "Для Каждого ... Из ..."
// и получать значения по ключу.
ВложенноеСоответствиеТемператур = СтруктураОтвета.temp;
Сообщить("Температура днем (из Соответствия): " + ВложенноеСоответствиеТемператур.Получить("day"));
Для Каждого ЭлементСоответствия Из ВложенноеСоответствиеТемператур Цикл
Сообщить("Ключ: " + ЭлементСоответствия.Ключ + ", Значение: " + Строка(ЭлементСоответствия.Значение));
КонецЦикла;
КонецЕсли;
Важный момент: распространенная ошибка заключается в попытке итерировать Структуру или Соответствие, как если бы это был Массив. Например, код Для каждого temp Из СтруктураОтвета.Получить("temp") Цикл приведет к ошибке, потому что СтруктураОтвета.Получить("temp") вернет Структуру (или Соответствие), а не Массив. Обход для Структуры или Соответствия осуществляется через пары "Ключ-Значение", как показано выше.
Если в JSON-ответе содержится массив объектов, например, "weather":[{"id":600,"main":"Snow",...}], то после парсинга функцией ПрочитатьJSON, этот элемент будет представлен в 1С как объект Массив. Каждый элемент этого массива, в свою очередь, будет являться Структурой или Соответствием.
Давайте посмотрим, как можно получить данные из массива "weather":
// СтруктураОтвета уже получена из предыдущего примера
// СтруктураОтвета.weather - это Массив
Если ТипЗнч(СтруктураОтвета.weather) = Тип("Массив") Тогда
МассивПогоды = СтруктураОтвета.weather;
Сообщить("Информация о погоде:");
Для Каждого ЭлементПогоды Из МассивПогоды Цикл
// Каждый ЭлементПогоды внутри МассиваПогоды - это Структура (или Соответствие)
Если ТипЗнч(ЭлементПогоды) = Тип("Структура") Тогда
Сообщить(" ID: " + ЭлементПогоды.id);
Сообщить(" Основное: " + ЭлементПогоды.main);
Сообщить(" Описание: " + ЭлементПогоды.description);
ИначеЕсли ТипЗнч(ЭлементПогоды) = Тип("Соответствие") Тогда
Сообщить(" ID: " + ЭлементПогоды.Получить("id"));
Сообщить(" Основное: " + ЭлементПогоды.Получить("main"));
Сообщить(" Описание: " + ЭлементПогоды.Получить("description"));
КонецЕсли;
КонецЦикла;
КонецЕсли;
В некоторых случаях структура JSON может быть очень сложной и содержать произвольную глубину вложенности. Заранее предсказать количество уровней бывает сложно, поэтому для таких ситуаций идеально подходит рекурсивный обход. Этот подход позволяет универсально обрабатывать как структуры (объекты), так и массивы, независимо от их глубины.
Рассмотрим JSON-ответ, приведенный в примере 12, который демонстрирует более глубокую вложенность:
"temperature":{"comfort":{"max":{"C":-11.7,"F":10.9},"min":{"C":-17,"F":1.4}}}
Для обработки такой структуры мы можем использовать рекурсивную процедуру:
&НаКлиенте
Процедура НачалоОбхода(Команда)
стрJSON = "
|{
| ""temperature"":{
| ""comfort"":{
| ""max"":{
| ""C"":-11.7,
| ""F"":10.9
| },
| ""min"":{
| ""C"":-17,
| ""F"":1.4
| }
| }
| }
|}";
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(стрJSON);
// Прочитаем JSON в объект 1С
Данные = ПрочитатьJSON(ЧтениеJSON);
// Запустим рекурсивный обход
ОбходРезультата(Данные);
ЧтениеJSON.Закрыть();
КонецПроцедуры
&НаКлиенте
Процедура ОбходРезультата(Ветка, Отступ = "")
Если ТипЗнч(Ветка) = Тип("Структура") Или ТипЗнч(Ветка) = Тип("Соответствие") Тогда
// Если текущий элемент - Структура или Соответствие, обходим его ключи и значения
Для Каждого ЭлементКоллекции Из Ветка Цикл
Если ТипЗнч(ЭлементКоллекции.Значение) = Тип("Структура")
Или ТипЗнч(ЭлементКоллекции.Значение) = Тип("Соответствие")
Или ТипЗнч(ЭлементКоллекции.Значение) = Тип("Массив") Тогда
// Если значение само является структурой, соответствием или массивом,
// выводим ключ и рекурсивно вызываем функцию для этого значения
Сообщить(Отступ + ЭлементКоллекции.Ключ + ":");
ОбходРезультата(ЭлементКоллекции.Значение, Отступ + " ");
Иначе
// Иначе, это конечное значение, выводим ключ и само значение
Сообщить(Отступ + ЭлементКоллекции.Ключ + ": " + Строка(ЭлементКоллекции.Значение));
КонецЕсли;
КонецЦикла;
ИначеЕсли ТипЗнч(Ветка) = Тип("Массив") Тогда
// Если текущий элемент - Массив, обходим его элементы
Для Каждого ЭлементМассива Из Ветка Цикл
// Для каждого элемента массива рекурсивно вызываем функцию
ОбходРезультата(ЭлементМассива, Отступ + " ");
КонецЦикла;
Иначе
// Если это простое значение (не структура, не соответствие, не массив)
Сообщить(Отступ + Строка(Ветка));
КонецЕсли;
КонецПроцедуры
Данная рекурсивная процедура ОбходРезультата позволяет нам:
Структура, Соответствие, Массив или простое значение).Структура или Соответствие, мы перебираем все пары "Ключ-Значение". Если значение само является сложным объектом (структурой, соответствием, массивом), мы рекурсивно вызываем ОбходРезультата для этого значения. В противном случае, это конечное значение, и мы его выводим.Массив, мы перебираем все его элементы и для каждого элемента рекурсивно вызываем ОбходРезультата.Таким образом, мы можем пройти по всей иерархии JSON-додокумента, не зная заранее его структуры, что делает этот метод универсальным и надежным.
После того как мы успешно спарсили JSON и получили данные в виде объектов 1С (Структура, Соответствие, Массив), следующая задача — занести их в табличное поле на форме или в ТаблицуЗначений. Табличное поле обычно связано с каким-либо источником данных, чаще всего с ТаблицейЗначений или динамическим списком.
Предположим, мы хотим создать ТаблицуЗначений и заполнить ее данными о погоде из массива "weather" или данными о температуре из объекта "temp". Для этого нам потребуется:
ТаблицаЗначений.ТаблицеЗначений, заполняя ее значениями.Рассмотрим пример заполнения ТаблицыЗначений данными из раздела "temp":
// ... (код для парсинга JSON и получения СтруктураОтвета, как показано выше)
ТаблицаТемператур = Новый ТаблицаЗначений;
// Добавляем колонки в ТаблицуЗначений
ТаблицаТемператур.Колонки.Добавить("День", Новый ОписаниеТипов("Число"));
ТаблицаТемператур.Колонки.Добавить("Минимум", Новый ОписаниеТипов("Число"));
ТаблицаТемператур.Колонки.Добавить("Максимум", Новый ОписаниеТипов("Число"));
ТаблицаТемператур.Колонки.Добавить("Ночь", Новый ОписаниеТипов("Число"));
ТаблицаТемператур.Колонки.Добавить("Вечер", Новый ОписаниеТипов("Число"));
ТаблицаТемператур.Колонки.Добавить("Утро", Новый ОписаниеТипов("Число"));
Если ТипЗнч(СтруктураОтвета.temp) = Тип("Структура") Тогда
ВложеннаяСтруктураТемператур = СтруктураОтвета.temp;
// Создаем новую строку в ТаблицеЗначений
НоваяСтрока = ТаблицаТемператур.Добавить();
// Заполняем колонки значениями из Структуры
НоваяСтрока.День = ВложеннаяСтруктураТемператур.day;
НоваяСтрока.Минимум = ВложеннаяСтруктураТемператур.min;
НоваяСтрока.Максимум = ВложеннаяСтруктураТемператур.max;
НоваяСтрока.Ночь = ВложеннаяСтруктураТемператур.night;
НоваяСтрока.Вечер = ВложеннаяСтруктураТемператур.eve;
НоваяСтрока.Утро = ВложеннаяСтруктураТемператур.morn;
ИначеЕсли ТипЗнч(СтруктураОтвета.temp) = Тип("Соответствие") Тогда
ВложенноеСоответствиеТемператур = СтруктураОтвета.temp;
НоваяСтрока = ТаблицаТемператур.Добавить();
НоваяСтрока.День = ВложенноеСоответствиеТемператур.Получить("day");
НоваяСтрока.Минимум = ВложенноеСоответствиеТемператур.Получить("min");
НоваяСтрока.Максимум = ВложенноеСоответствиеТемператур.Получить("max");
НоваяСтрока.Ночь = ВложенноеСоответствиеТемператур.Получить("night");
НоваяСтрока.Вечер = ВложенноеСоответствиеТемператур.Получить("eve");
НоваяСтрока.Утро = ВложенноеСоответствиеТемператур.Получить("morn");
КонецЕсли;
// Теперь ТаблицаТемператур заполнена и может быть присвоена в качестве
// источника данных табличного поля на форме.
// Например: Элементы.МоеТабличноеПоле.Значение = ТаблицаТемператур;
Аналогичным образом мы можем заполнить ТаблицуЗначений данными из массива "weather":
// ... (код для парсинга JSON и получения СтруктураОтвета)
ТаблицаПогоды = Новый ТаблицаЗначений;
ТаблицаПогоды.Колонки.Добавить("ID", Новый ОписаниеТипов("Число"));
ТаблицаПогоды.Колонки.Добавить("Основное", Новый ОписаниеТипов("Строка"));
ТаблицаПогоды.Колонки.Добавить("Описание", Новый ОписаниеТипов("Строка"));
ТаблицаПогоды.Колонки.Добавить("Иконка", Новый ОписаниеТипов("Строка")); // Добавим для полноты
Если ТипЗнч(СтруктураОтвета.weather) = Тип("Массив") Тогда
МассивПогоды = СтруктураОтвета.weather;
Для Каждого ЭлементПогоды Из МассивПогоды Цикл
НоваяСтрока = ТаблицаПогоды.Добавить();
Если ТипЗнч(ЭлементПогоды) = Тип("Структура") Тогда
НоваяСтрока.ID = ЭлементПогоды.id;
НоваяСтрока.Основное = ЭлементПогоды.main;
НоваяСтрока.Описание = ЭлементПогоды.description;
НоваяСтрока.Иконка = ЭлементПогоды.icon; // Предполагаем, что icon есть в каждом объекте
ИначеЕсли ТипЗнч(ЭлементПогоды) = Тип("Соответствие") Тогда
НоваяСтрока.ID = ЭлементПогоды.Получить("id");
НоваяСтрока.Основное = ЭлементПогоды.Получить("main");
НоваяСтрока.Описание = ЭлементПогоды.Получить("description");
НоваяСтрока.Иконка = ЭлементПогоды.Получить("icon");
КонецЕсли;
КонецЦикла;
КонецЕсли;
// ТаблицаПогоды также может быть присвоена табличному полю.
Для более сложных и глубоких структур, когда мы используем рекурсивный обход, заполнение ТаблицыЗначений потребует модификации рекурсивной функции. Например, можно передавать ТаблицуЗначений в рекурсивную функцию и добавлять строки, когда достигаются конечные данные нужного уровня вложенности.
Помимо рассмотренных методов, важно учитывать несколько других аспектов при работе с JSON в 1С:
Мы использовали объектный подход с функцией ПрочитатьJSON, который удобен для относительно небольших JSON-объектов. Однако, если вы работаете с очень большими JSON-файлами (десятки и сотни мегабайт) или ответами, то ПрочитатьJSON может привести к значительным затратам памяти и производительности, так как весь документ загружается в память. В таких случаях более эффективным будет потоковый подход с использованием объектов ЧтениеJSON и ЗаписьJSON для чтения документа по частям, не загружая его целиком.
Смешанный подход также возможен: используйте потоковое чтение для навигации по документу до нужного большого раздела, а затем прочитайте этот раздел объектно.
JSON поддерживает строки, числа, булевы значения, объекты, массивы, а также литералы true, false и null. 1С автоматически преобразует эти значения в соответствующие типы. Однако с датами есть нюансы. JSON не имеет встроенного типа "дата", и они обычно передаются в виде строк (например, в формате ISO 8601). ПрочитатьJSON по умолчанию может не всегда корректно преобразовать строковые даты в тип "Дата" 1С. В таких случаях вам потребуется вручную парсить строковое представление даты, используя функции 1С, например, Дата() или СтрокаВДату(), после получения значения как строки.
При работе с внешними данными всегда есть риск получения некорректного JSON или ошибки при обмене. Важно обернуть код парсинга в конструкцию Попытка...Исключение, чтобы graceful обрабатывать возможные ошибки, например, при неверном формате JSON.
Попытка
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(СтрокаОтвета);
СтруктураОтвета = ПрочитатьJSON(ЧтениеJSON);
ЧтениеJSON.Закрыть();
Исключение
Сообщить("Ошибка при парсинге JSON: " + ОписаниеОшибки());
Возврат;
КонецПопытки;
Для более тонкой настройки сериализации и десериализации JSON, особенно при работе с датами или другими специфическими типами, можно использовать объекты СериализацияJSON и НастройкиСериализацииJSON. Они позволяют задавать форматы вывода, например, для дат, а также управлять тем, какие свойства объектов сериализуются.
Работа с JSON в 1С — это неотъемлемая часть интеграции с внешними системами. Мы рассмотрели основные подходы к парсингу JSON: от простого доступа к вложенным структурам и массивам до универсального рекурсивного обхода для произвольной вложенности. Мы также показали, как преобразовать спарсенные данные в ТаблицуЗначений для последующего отображения в табличном поле на форме. Помните о выборе подхода (объектный/потоковый) в зависимости от объема данных и всегда предусматривайте обработку ошибок для создания надежных решений.