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

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

При работе с данными из внешних источников, часто приходится разбирать (парсить) 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" от второго и третьего объектов перезаписывает значение первого.

Решение 1: Потоковое чтение с управлением состоянием

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

Чтобы избежать перезаписи данных, можно использовать переменную-флаг. Логика будет следующей:

  1. Начинаем читать JSON в цикле.
  2. Когда встречаем пару "type": "fbs", взводим наш флаг (например, сохраняем значение "fbs" в переменную).
  3. Когда встречаем ключ "present", проверяем состояние флага.
  4. Если флаг взведен (т.е. мы находимся внутри нужного нам объекта), записываем значение "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-структур логика флагов может превратиться в запутанный клубок. Существует гораздо более простое и элегантное решение.

Решение 2: Объектный подход через `ПрочитатьJSON` (Рекомендуемый способ)

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

Преимущества этого подхода очевидны:

Разберем по шагам, как решить нашу задачу этим способом:

  1. Создаем объект ЧтениеJSON и передаем ему файл или строку.
  2. Вызываем функцию ПрочитатьJSON(), которая вернет нам готовую структуру данных 1С.
  3. В полученной структуре обращаемся к свойству stocks, которое будет содержать массив.
  4. Берем первый элемент массива по индексу [0].
  5. Заполняем строку табличной части данными из этого элемента.

Пример кода:


&НаКлиенте
Процедура ЗапросНовымСпособом(Команда)
    Объект.ТЧДанные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-файлов размером в несколько гигабайт, когда загрузка всего файла в память невозможна.

← На главную