Как получить полный путь до значения в JSON при помощи перебора в 1С

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

Работа с JSON-структурами в 1С:Предприятии — одна из наиболее частых задач при интеграции с внешними сервисами и API. Зачастую нам недостаточно просто прочитать данные; нам нужно знать точный «адрес» или путь к конкретному значению, особенно если структура сложная, многоуровневая и содержит вложенные массивы. В этой статье мы разберем, как автоматизировать процесс получения полных путей ко всем элементам JSON-документа.

Давайте проанализируем ситуацию: у нас есть объект, содержащий вложенные структуры и массивы. Наша задача — получить список строк вида MainService.MainService.Service.Name. Мы рассмотрим несколько эффективных способов реализации этой задачи, включая рекурсивный обход и потоковое чтение.

Способ 1: Рекурсивный обход универсальных коллекций

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

Рассмотрим по шагам, как это работает. Функция должна принимать два параметра: текущий узел (объект или массив) и накопленный к этому моменту путь.


// Основная процедура запуска
Процедура ПолучитьПутиИзJSON(ТекстJSON)
    Чтение = Новый ЧтениеJSON;
    Чтение.УстановитьСтроку(ТекстJSON);
    
    // Читаем JSON в Соответствие для сохранения регистра ключей
    Данные = ПрочитатьJSON(Чтение, Ложь); 
    Чтение.Закрыть();
    
    ОбходЭлементовРекурсивно(Данные, "");
КонецПроцедуры

// Рекурсивная функция обхода
Процедура ОбходЭлементовРекурсивно(ТекущийУзел, ТекущийПуть)
    
    ТипУзла = ТипЗнч(ТекущийУзел);
    
    Если ТипУзла = Тип("Соответствие") Или ТипУзла = Тип("Структура") Тогда
        
        Для Каждого Элемент Из ТекущийУзел Цикл
            НовыйПуть = ?(ПустаяСтрока(ТекущийПуть), Элемент.Ключ, ТекущийПуть + "." + Элемент.Ключ);
            ОбходЭлементовРекурсивно(Элемент.Значение, НовыйПуть);
        КонецЦикла;
        
    ИначеЕсли ТипУзла = Тип("Массив") Тогда
        
        // Для массивов добавляем индекс в квадратных скобках
        Для Индекс = 0 По ТекущийУзел.Количество() - 1 Цикл
            НовыйПуть = ТекущийПуть + "[" + Формат(Индекс, "ЧГ=0") + "]";
            ОбходЭлементовРекурсивно(ТекущийУзел[Индекс], НовыйПуть);
        КонецЦикла;
        
    Иначе
        // Мы дошли до конечного значения (строка, число, дата, булево)
        Сообщить(ТекущийПуть + " = " + ТекущийУзел);
    КонецЕсли;
    
КонецПроцедуры

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

Способ 2: Потоковое чтение через объект ЧтениеJSON

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

Выясним причину эффективности этого метода: он не создает дерево объектов в памяти, а просто сообщает о событиях (начало объекта, имя свойства, значение). Для отслеживания пути нам понадобится стек (обычный Массив).

  1. При событии ИмяСвойства мы запоминаем текущий ключ.
  2. При событии НачалоОбъекта или НачалоМассива мы «проваливаемся» глубже.
  3. При возникновении события КонецОбъекта мы удаляем последний элемент из стека (возвращаемся на уровень выше).

Процедура ПолучитьПутиПотоком(ТекстJSON)
    Чтение = Новый ЧтениеJSON;
    Чтение.УстановитьСтроку(ТекстJSON);
    
    СтекПути = Новый Массив;
    ТекущееИмяСвойства = "";
    
    Пока Чтение.Прочитать() Цикл
        
        ТипУзла = Чтение.ТипТекущегоУзла;
        
        Если ТипУзла = ТипУзлаJSON.ИмяСвойства Тогда
            ТекущееИмяСвойства = Чтение.ТекущееЗначение;
            
        ИначеЕсли ТипУзла = ТипУзлаJSON.НачалоОбъекта Тогда
            Если Не ПустаяСтрока(ТекущееИмяСвойства) Тогда
                СтекПути.Добавить(ТекущееИмяСвойства);
                ТекущееИмяСвойства = "";
            КонецЕсли;
            
        ИначеЕсли ТипУзла = ТипУзлаJSON.КонецОбъекта Тогда
            Если СтекПути.Количество() > 0 Тогда
                СтекПути.Удалить(СтекПути.Количество() - 1);
            КонецЕсли;
            
        ИначеЕсли ТипУзла = ТипУзлаJSON.Число 
            Или ТипУзла = ТипУзлаJSON.Строка 
            Или ТипУзла = ТипУзлаJSON.Булево Тогда
            
            Путь = СтрСоединить(СтекПути, ".");
            ПолныйПуть = Путь + ?(ПустаяСтрока(Путь), "", ".") + ТекущееИмяСвойства;
            Сообщить(ПолныйПуть + " : " + Чтение.ТекущееЗначение);
        КонецЕсли;
        
    КонецЦикла;
    Чтение.Закрыть();
КонецПроцедуры

Важные нюансы обработки массивов

В исходном примере автора пути выглядят так: MainService.MainService.Id. Однако важно понимать, что MainService в данном JSON является массивом (на что указывают квадратные скобки []). Если в массиве будет несколько элементов, путь без индекса станет неоднозначным.

Рассмотрим подробнее: стандартная нотация для доступа к элементам JSON требует указания индекса, например, MainService[0].Id. Если ваша задача — просто перечислить возможные имена полей для схемы, можно игнорировать индексы. Но если вы строите путь для извлечения данных, обязательно добавьте логику обработки индексов, как показано в рекурсивном примере выше.

Визуализация через Дерево значений

Для удобства отладки или предоставления пользователю возможности выбора нужного поля, мы можем загрузить JSON в объект ДеревоЗначений. Разберем по шагам, как это реализовать:

  1. Создаем ДеревоЗначений с колонками Имя, Значение и ПолныйПуть.
  2. При рекурсивном обходе создаем новые строки дерева, устанавливая текущую строку как родителя для вложенных узлов.
  3. В колонку ПолныйПуть записываем вычисленное значение.

Такой подход позволяет использовать стандартные методы НайтиСтроки() для быстрого поиска пути к конкретному значению или идентификатору Id по всей структуре документа.

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

← На главную