При работе с данными из внешних источников, часто приходится разбирать (парсить) JSON-файлы или строки. Стандартная задача — прочитать данные и загрузить их в табличную часть документа или справочника в 1С. Однако могут возникнуть сложности, если JSON содержит массив однотипных объектов, а нам нужны данные только из первого элемента этого массива. Рассмотрим, как решить эту задачу правильно, избежав распространенной ошибки перезаписи данных.
Проанализируем ситуацию: у нас есть JSON-файл со структурой, где ключ "stocks" содержит массив объектов. Каждый объект описывает остатки на разных складах (fbs, fbo и т.д.) и имеет одинаковые ключи: "type", "present", "reserved".
"stocks": [
{ "type": "fbs", "present": 10, "reserved": 0 },
{ "type": "fbo", "present": 11, "reserved": 0 },
{ "type": "crossborder", "present": 12, "reserved": 0 }
]
Задача — записать в таблицу 1С данные только по первому складу, то есть где "type": "fbs". При наивном подходе с потоковым чтением возникает проблема: цикл проходит по всем элементам массива, и значение ключа "present" от второго и третьего объектов перезаписывает значение первого.
Первый подход, который часто приходит в голову, — это последовательное (потоковое) чтение JSON с помощью объекта ЧтениеJSON. Этот метод хорош для очень больших файлов, так как не загружает весь файл в память. Однако для решения нашей задачи он требует дополнительной логики для отслеживания текущего состояния парсера.
Чтобы избежать перезаписи данных, можно использовать переменную-флаг. Логика будет следующей:
"type": "fbs", взводим наш флаг (например, сохраняем значение "fbs" в переменную)."present", проверяем состояние флага."present" в таблицу и сразу же сбрасываем флаг.Сброс флага — ключевой момент. Он гарантирует, что значения "present" из последующих объектов массива (fbo, crossborder) будут проигнорированы.
Посмотрим на пример кода, реализующий этот подход:
&НаКлиенте
Процедура Запрос(Команда)
Объект.ТЧДанныеJS.Очистить;
СоответствиеПолей = Новый Соответствие;
СоответствиеПолей.Вставить("offer_id", "Артикул");
СоответствиеПолей.Вставить("type", "Наименование");
СоответствиеПолей.Вставить("present", "Количество");
ЧтениеJS = Новый ЧтениеJSON;
ЧтениеJS.ОткрытьФайл("C:\www\response.json");
ИмяТекущегоСвойстваJS = "";
type = ""; // Наша переменная-флаг
Пока ЧтениеJS.Прочитать() Цикл
ТипJS = ЧтениеJS.ТипТекущегоЗначения;
// Считываем имя свойства (ключ)
Если ТипJS = ТипЗначенияJSON.ИмяСвойства Тогда
ИмяТекущегоСвойстваJS = ЧтениеJS.ТекущееЗначение;
Если ИмяТекущегоСвойстваJS = "offer_id" Тогда
СтрокаТЧ = Объект.ТЧДанныеJS.Добавить();
КонецЕсли;
// Считываем значение свойства
ИначеЕсли ТипJS = ТипЗначенияJSON.Строка ИЛИ ТипJS = ТипЗначенияJSON.Число Тогда
// Обрабатываем "offer_id"
Если ИмяТекущегоСвойстваJS = "offer_id" Тогда
ИмяРеквизитаТЧ = СоответствиеПолей.Получить(ИмяТекущегоСвойстваJS);
Если ИмяРеквизитаТЧ <> Неопределено Тогда
СтрокаТЧ[ИмяРеквизитаТЧ] = ЧтениеJS.ТекущееЗначение;
КонецЕсли;
// Обрабатываем "type" и взводим флаг
ИначеЕсли ИмяТекущегоСвойстваJS = "type" И ЧтениеJS.ТекущееЗначение = "fbs" Тогда
type = ЧтениеJS.ТекущееЗначение; // Взводим флаг
ИмяРеквизитаТЧ = СоответствиеПолей.Получить(ИмяТекущегоСвойстваJS);
Если ИмяРеквизитаТЧ <> Неопределено Тогда
СтрокаТЧ[ИмяРеквизитаТЧ] = ЧтениеJS.ТекущееЗначение;
КонецЕсли;
// Обрабатываем "present", проверяя флаг
ИначеЕсли ИмяТекущегоСвойстваJS = "present" Тогда
Если type = "fbs" Тогда // Проверяем флаг
ИмяРеквизитаТЧ = СоответствиеПолей.Получить(ИмяТекущегоСвойстваJS);
Если ИмяРеквизитаТЧ <> Неопределено Тогда
СтрокаТЧ[ИмяРеквизитаТЧ] = ЧтениеJS.ТекущееЗначение;
type = ""; // Сбрасываем флаг, чтобы больше не записывать "present"
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЦикла;
ЧтениеJS.Закрыть();
КонецПроцедуры
Этот метод работает, но его можно назвать "костылем". Код становится сложнее для чтения и поддержки, а для более вложенных JSON-структур логика флагов может превратиться в запутанный клубок. Существует гораздо более простое и элегантное решение.
Для большинства задач, особенно при работе с API, где объемы данных не гигантские, предпочтительным является объектный подход. Он заключается в полной десериализации JSON-строки в нативные объекты 1С: Структуру, Массив и Соответствие. Это делается с помощью одной функции — ПрочитатьJSON().
Преимущества этого подхода очевидны:
[0] или последнему), а не только к тому, который соответствует сложному условию в цикле.Разберем по шагам, как решить нашу задачу этим способом:
ЧтениеJSON и передаем ему файл или строку.ПрочитатьJSON(), которая вернет нам готовую структуру данных 1С.stocks, которое будет содержать массив.[0].Пример кода:
&НаКлиенте
Процедура ЗапросНовымСпособом(Команда)
Объект.ТЧДанныеJS.Очистить();
// Шаг 1-2: Читаем JSON и десериализуем его в объекты 1С
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.ОткрытьФайл("C:\www\response.json");
// или для HTTP-ответа: ЧтениеJSON.УстановитьСтроку(Ответ.ПолучитьТелоКакСтроку());
Данные = ПрочитатьJSON(ЧтениеJSON);
ЧтениеJSON.Закрыть();
// В переменной "Данные" теперь структура 1С.
// Свойство "stocks" этой структуры содержит массив.
// Шаг 3: Проверяем, что массив не пустой
Если ТипЗнч(Данные.stocks) = Тип("Массив") И Данные.stocks.Количество() > 0 Тогда
// Шаг 4: Получаем первый объект из массива
ПервыйОбъект = Данные.stocks[0];
// Опционально: проверяем, что это действительно нужный нам тип
Если ПервыйОбъект.type = "fbs" Тогда
// Шаг 5: Добавляем строку и заполняем ее данными
НоваяСтрока = Объект.ТЧДанныеJS.Добавить();
// Заполняем Артикул (предполагаем, что он на том же уровне, что и stocks)
НоваяСтрока.Артикул = Данные.offer_id;
// Заполняем остальные данные из первого объекта
НоваяСтрока.Наименование = ПервыйОбъект.type;
НоваяСтрока.Количество = ПервыйОбъект.present;
// Более универсальный способ заполнения с помощью ЗаполнитьЗначенияСвойств:
// ЗаполнитьЗначенияСвойств(НоваяСтрока, ПервыйОбъект, "type, present");
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Как видите, этот код значительно чище и его логика прозрачна. Мы напрямую говорим системе, что нам нужен нулевой элемент массива stocks, и работаем с его свойствами.
По умолчанию ПрочитатьJSON() преобразует JSON-объекты в Структуры 1С. Это удобно, так как к свойствам можно обращаться через точку (например, ПервыйОбъект.type). Однако имена свойств в Структуре 1С имеют ограничения: они не могут содержать пробелы, дефисы, начинаться с цифры и т.д.
Если в вашем JSON ключи могут иметь "нестандартные" имена (например, "offer-id"), то при чтении нужно использовать второй параметр функции ПрочитатьJSON(), установив его в Истина. В этом случае JSON-объект будет преобразован в Соответствие.
// Второй параметр "Истина" указывает, что нужно использовать "Соответствие"
Данные = ПрочитатьJSON(ЧтениеJSON, Истина);
// Обращение к данным будет через квадратные скобки
Артикул = Данные["offer-id"];
В итоге, для решения задачи чтения данных только из первого объекта JSON-массива, настоятельно рекомендуется использовать объектный подход с функцией ПрочитатьJSON(). Он делает код простым, надежным и легким для дальнейшей поддержки. Потоковое чтение стоит применять только в специфических случаях, например, при обработке JSON-файлов размером в несколько гигабайт, когда загрузка всего файла в память невозможна.