Как правильно парсить вложенные JSON-объекты и массивы в 1С и заполнять табличное поле?

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

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

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

Основы парсинга JSON в 1С с помощью ПрочитатьJSON

Для начала давайте рассмотрим основной инструмент для чтения JSON в 1С — функцию ПрочитатьJSON. Она позволяет загрузить весь JSON-додокумент в память и преобразовать его в стандартные объекты 1С: Структура, Соответствие или Массив.

  1. Создаем объект ЧтениеJSON: Это первый шаг для инициализации процесса чтения.
  2. Устанавливаем строку JSON: Передаем полученный от сервера JSON-ответ в метод УстановитьСтроку.
  3. Вызываем ПрочитатьJSON: Эта функция непосредственно парсит строку и возвращает результат в виде объекта 1С.
  4. Закрываем Чтение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-объектов (например, "temp")

После вызова функции Прочитать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")

Если в JSON-ответе содержится массив объектов, например, "weather":[{"id":600,"main":"Snow",...}], то после парсинга функцией ПрочитатьJSON, этот элемент будет представлен в 1С как объект Массив. Каждый элемент этого массива, в свою очередь, будет являться Структурой или Соответствием.

Давайте посмотрим, как можно получить данные из массива "weather":


// СтруктураОтвета уже получена из предыдущего примера
// СтруктураОтвета.weather - это Массив

Если ТипЗнч(СтруктураОтвета.weather) = Тип("Массив") Тогда
    МассивПогоды = СтруктураОтвета.weather;

    Сообщить("Информация о погоде:");
    Для Каждого ЭлементПогоды Из МассивПогоды Цикл
        // Каждый ЭлементПогоды внутри МассиваПогоды - это Структура (или Соответствие)
        Если ТипЗнч(ЭлементПогоды) = Тип("Структура") Тогда
            Сообщить("  ID: " + ЭлементПогоды.id);
            Сообщить("  Основное: " + ЭлементПогоды.main);
            Сообщить("  Описание: " + ЭлементПогоды.description);
        ИначеЕсли ТипЗнч(ЭлементПогоды) = Тип("Соответствие") Тогда
            Сообщить("  ID: " + ЭлементПогоды.Получить("id"));
            Сообщить("  Основное: " + ЭлементПогоды.Получить("main"));
            Сообщить("  Описание: " + ЭлементПогоды.Получить("description"));
        КонецЕсли;
    КонецЦикла;
КонецЕсли;

Универсальный рекурсивный обход для любой вложенности JSON

В некоторых случаях структура 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.Закрыть();
КонецПроцедуры

&НаКлиенте
Процедура ОбходРезультата(Ветка, Отступ = "")
    Если ТипЗнч(Ветка) = Тип("Структура") Или ТипЗнч(Ветка) = Тип("Соответствие") Тогда
        // Если текущий элемент - Структура или Соответствие, обходим его ключи и значения
        Для Каждого ЭлементКоллекции Из Ветка Цикл
            Если ТипЗнч(ЭлементКоллекции.Значение) = Тип("Структура") 
                Или ТипЗнч(ЭлементКоллекции.Значение) = Тип("Соответствие") 
                Или ТипЗнч(ЭлементКоллекции.Значение) = Тип("Массив") Тогда
                
                // Если значение само является структурой, соответствием или массивом,
                // выводим ключ и рекурсивно вызываем функцию для этого значения
                Сообщить(Отступ + ЭлементКоллекции.Ключ + ":");
                ОбходРезультата(ЭлементКоллекции.Значение, Отступ + "  ");
            Иначе
                // Иначе, это конечное значение, выводим ключ и само значение
                Сообщить(Отступ + ЭлементКоллекции.Ключ + ": " + Строка(ЭлементКоллекции.Значение));
            КонецЕсли;
        КонецЦикла;
    ИначеЕсли ТипЗнч(Ветка) = Тип("Массив") Тогда
        // Если текущий элемент - Массив, обходим его элементы
        Для Каждого ЭлементМассива Из Ветка Цикл
            // Для каждого элемента массива рекурсивно вызываем функцию
            ОбходРезультата(ЭлементМассива, Отступ + "  ");
        КонецЦикла;
    Иначе
        // Если это простое значение (не структура, не соответствие, не массив)
        Сообщить(Отступ + Строка(Ветка));
    КонецЕсли;
КонецПроцедуры

Данная рекурсивная процедура ОбходРезультата позволяет нам:

  1. Определять тип текущего элемента (Структура, Соответствие, Массив или простое значение).
  2. Если это Структура или Соответствие, мы перебираем все пары "Ключ-Значение". Если значение само является сложным объектом (структурой, соответствием, массивом), мы рекурсивно вызываем ОбходРезультата для этого значения. В противном случае, это конечное значение, и мы его выводим.
  3. Если это Массив, мы перебираем все его элементы и для каждого элемента рекурсивно вызываем ОбходРезультата.
  4. Для простых значений (числа, строки, булевы), мы просто выводим их.

Таким образом, мы можем пройти по всей иерархии JSON-додокумента, не зная заранее его структуры, что делает этот метод универсальным и надежным.

Заполнение табличного поля 1С данными из JSON

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

Предположим, мы хотим создать ТаблицуЗначений и заполнить ее данными о погоде из массива "weather" или данными о температуре из объекта "temp". Для этого нам потребуется:

  1. Создать новый объект ТаблицаЗначений.
  2. Добавить в нее необходимые колонки, соответствующие полям JSON-объектов.
  3. Итерировать по спарсенным JSON-данным и для каждого элемента создавать новую строку в ТаблицеЗначений, заполняя ее значениями.

Рассмотрим пример заполнения ТаблицыЗначений данными из раздела "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С:

1. Объектный vs. Потоковый подход

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

Смешанный подход также возможен: используйте потоковое чтение для навигации по документу до нужного большого раздела, а затем прочитайте этот раздел объектно.

2. Типы данных и даты

JSON поддерживает строки, числа, булевы значения, объекты, массивы, а также литералы true, false и null. 1С автоматически преобразует эти значения в соответствующие типы. Однако с датами есть нюансы. JSON не имеет встроенного типа "дата", и они обычно передаются в виде строк (например, в формате ISO 8601). ПрочитатьJSON по умолчанию может не всегда корректно преобразовать строковые даты в тип "Дата" 1С. В таких случаях вам потребуется вручную парсить строковое представление даты, используя функции 1С, например, Дата() или СтрокаВДату(), после получения значения как строки.

3. Обработка ошибок

При работе с внешними данными всегда есть риск получения некорректного JSON или ошибки при обмене. Важно обернуть код парсинга в конструкцию Попытка...Исключение, чтобы graceful обрабатывать возможные ошибки, например, при неверном формате JSON.


Попытка
    ЧтениеJSON = Новый ЧтениеJSON;
    ЧтениеJSON.УстановитьСтроку(СтрокаОтвета);
    СтруктураОтвета = ПрочитатьJSON(ЧтениеJSON);
    ЧтениеJSON.Закрыть();
Исключение
    Сообщить("Ошибка при парсинге JSON: " + ОписаниеОшибки());
    Возврат;
КонецПопытки;

4. СериализацияJSON и НастройкиСериализацииJSON

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

Заключение

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

← На главную