Как потоково читать огромные JSON-ответы в 1С при GET-запросах

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

При интеграции 1С с внешними системами разработчики часто сталкиваются с ситуацией, когда API стороннего сервиса возвращает огромный массив данных в формате JSON, но не поддерживает пагинацию. Если попытаться получить такой ответ стандартным методом Ответ.ПолучитьТелоКакСтроку(), система может аварийно завершиться с ошибкой "Недостаточно памяти", так как строка в несколько сотен миллионов символов требует колоссального объема RAM. Разберем, как правильно организовать потоковую обработку данных, чтобы минимизировать нагрузку на сервер или клиент.

Причина проблемы: Почему 1С «падает» на больших запросах

Проанализируем ситуацию: когда мы вызываем метод Получить(), платформа 1С по умолчанию пытается загрузить все тело ответа в оперативную память. Если объем данных составляет, например, 200 МБ, то в кодировке Unicode в памяти 1С эта строка может занять 400 МБ. Однако на этом расходы не заканчиваются. При парсинге этого текста в объекты Структура или Соответствие через функцию ПрочитатьJSON() потребление памяти возрастает в 5-10 раз из-за создания множества объектов платформы. Для 32-битного клиента (предел около 2 ГБ) это почти всегда фатально.

Решение 1: Получение данных во временный файл

Первый и самый простой способ избежать переполнения памяти при получении данных — это запись ответа напрямую на диск, минуя оперативную память. Рассмотрим, как это реализовать с помощью метода Получить() объекта HTTPСоединение.

В этом случае данные «стримятся» сразу в файл. Посмотрим на пример кода:


// Подготавливаем соединение и запрос
Соединение = Новый HTTPСоединение("api.service.com",,,, Неопределено, 60);
Запрос = Новый HTTPЗапрос("/v1/get_huge_data");

// Генерируем имя временного файла, куда будет записан ответ
ИмяФайла = ПолучитьИмяВременногоФайла("json");

// Ключевой момент: указываем имя файла вторым параметром
Ответ = Соединение.Получить(Запрос, ИмяФайла);

Если Ответ.КодСостояния = 200 Тогда
    // Теперь данные на диске, а не в памяти.
    // Важно: НЕ используйте ТекстовыйДокумент.Прочитать() или Ответ.ПолучитьТелоКакСтроку()!
    // Читайте файл только через специализированные потоковые инструменты.
    ОбработатьФайлJSON(ИмяФайла);
КонецЕсли;

УдалитьФайлы(ИмяФайла);

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

Решение 2: Потоковое чтение тела ответа (Stream)

Начиная с современных версий платформы 8.3, 1С позволяет работать с телом HTTP-ответа как с потоком. Это наиболее элегантный и производительный способ, так как он не требует создания временных файлов на диске (в некоторых случаях) и позволяет начать парсинг еще до того, как весь ответ будет полностью загружен из сети.

Рассмотрим пример использования метода ПолучитьТелоКакПоток():


Ответ = Соединение.Получить(Запрос);

Если Ответ.КодСостояния = 200 Тогда
    Чтение = Новый ЧтениеJSON;
    // Открываем поток чтения напрямую из тела HTTP-ответа
    Чтение.ОткрытьПоток(Ответ.ПолучитьТелоКакПоток());
    
    // Вариант А: Быстрое чтение, если памяти достаточно
    // ДанныеОтвета = ПрочитатьJSON(Чтение, Истина);
    
    // Вариант Б: Истинное потоковое (событийное) чтение
    Пока Чтение.Прочитать() Цикл
        ТипУзла = Чтение.ТипТекущегоУзла;
        Если ТипУзла = ТипУзлаЧтенияJSON.ИмяСвойства И Чтение.ТекущееЗначение = "Items" Тогда
            // Нашли начало массива элементов, обрабатываем их по одному
            Пока Чтение.Прочитать() Цикл
                Если Чтение.ТипТекущегоУзла = ТипУзлаЧтенияJSON.КонецМассива Тогда
                    Прервать;
                КонецЕсли;
                
                // Читаем один объект из массива, не загружая остальные
                ОбъектДанных = ПрочитатьJSON(Чтение); 
                ОбработатьОбъект(ОбъектДанных);
            КонецЦикла;
        КонецЕсли;
    КонецЦикла;
    
    Чтение.Закрыть();
КонецЕсли;

Решение 3: Событийная модель парсинга (SAX-подобный подход)

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

Чтобы этого избежать, мы используем цикл Пока ЧтениеJSON.Прочитать() Цикл. В этом режиме мы «бежим» по узлам JSON:

  1. Пропускаем мета-данные в начале файла.
  2. Доходим до начала основного массива данных.
  3. В цикле считываем только один объект текущего элемента массива.
  4. Записываем этот объект в базу данных (или обрабатываем).
  5. Переходим к следующему элементу.

Таким образом, в памяти одновременно находится только одна небольшая структура, независимо от того, весит ли исходный JSON 100 МБ или 10 ГБ.

Дополнительные советы по оптимизации

Проанализируем еще несколько технических нюансов, которые помогут ускорить работу с большими данными:

Подведем итог: для работы с большими ответами всегда используйте HTTPСоединение.Получить(...) с записью в файл или ПолучитьТелоКакПоток(). Никогда не превращайте тело ответа в одну гигантскую строку и используйте последовательное чтение JSON через цикл, чтобы сохранить стабильность вашей системы.

← На главную