1С: ЗаписьJSON - потоковое чтение и запись больших JSON файлов
Архитектура работы с памятью и проблема DOM-парсинга
При обмене огромными объемами данных (например, выгрузка номенклатуры с миллионом позиций на сайт или загрузка маркетплейс-прайсов) через формат JSON разработчики 1С часто используют простые методы: формирование структуры (Дерева Значений, Массива) в памяти с последующим вызовом ЗаписатьJSON(Объект, ...) или использование функции ПрочитатьJSON(). Это DOM-модель: она пытается загрузить весь документ в оперативную память (RAM) рабочего процесса rphost.exe. Результат: экспоненциальный рост потребления памяти, падение процесса по лимиту (Ограничение памяти на рабочий процесс), зависание фоновых заданий и ошибка «Недостаточно памяти». Бизнес-риски: остановка интеграций, срыв обмена с внешними системами (API, интернет-магазины).
Сравнение методов обработки JSON в 1С
| Метод (Модель) | Плюсы | Минусы (Риски) |
|---|---|---|
DOM-сериализация (ПрочитатьJSON) | Простота кода (1-2 строки). | Memory Leak. Файл 100 МБ займет 500-1000 МБ в ОЗУ. |
Стриминг (ЗаписьJSON / ЧтениеJSON) | RAM-friendly. Потребление памяти < 10 МБ независимо от размера файла. | Сложность кода (нужно писать циклы, управлять объектами и свойствами). |
Алгоритмы потоковой обработки (Streaming)
Сценарий 1: Потоковая запись JSON (Стриминг в файл)
Вместо формирования гигантского массива, пишем элементы по одному прямо в дисковый поток.
ЗаписьJSON = Новый ЗаписьJSON;
ПараметрыЗаписи = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Нет);
ЗаписьJSON.ОткрытьФайл("C:\temp\export.json", , , ПараметрыЗаписи);
ЗаписьJSON.ЗаписатьНачалоМассива(); // Начинаем массив товаров
Пока ВыборкаТоваров.Следующий() Цикл
ЗаписьJSON.ЗаписатьНачалоОбъекта(); // {
ЗаписьJSON.ЗаписатьИмяСвойства("SKU");
ЗаписьJSON.ЗаписатьЗначение(ВыборкаТоваров.Артикул);
ЗаписьJSON.ЗаписатьИмяСвойства("Price");
ЗаписьJSON.ЗаписатьЗначение(ВыборкаТоваров.Цена);
ЗаписьJSON.ЗаписатьКонецОбъекта(); // }
КонецЦикла;
ЗаписьJSON.ЗаписатьКонецМассива(); // Завершаем массив
ЗаписьJSON.Закрыть(); // Сброс буферов на дискСценарий 2: Потоковое чтение JSON (SAX-подобный парсинг)
Читаем узлы последовательно, не загружая файл целиком.
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.ОткрытьФайл("C:\temp\import.json");
Пока ЧтениеJSON.Прочитать() Цикл
ТипУзла = ЧтениеJSON.ТипТекущегоЗначения;
Если ТипУзла = ТипЗначенияJSON.ИмяСвойства И ЧтениеJSON.ТекущееЗначение = "SKU" Тогда
ЧтениеJSON.Прочитать(); // Переходим к значению свойства
Сообщить("Обработка артикула: " + ЧтениеJSON.ТекущееЗначение);
// Вызов логики обработки товара...
КонецЕсли;
КонецЦикла;
ЧтениеJSON.Закрыть();Типовые ошибки программистов 1С
- Попытка смешивать стриминг и DOM: Использование
ЗаписатьJSON()внутри цикла обхода выборки перезапишет файл (или сформирует невалидный JSON с множеством корневых элементов), а не добавит данные в конец. - Забытый метод Закрыть(): Объект ЗаписьJSON держит эксклюзивную блокировку на файле (File Handle) на уровне ОС и буферизирует данные. Без вызова
Закрыть()файл останется заблокированным и неполным.
Оптимизация высоконагруженных интеграций требует профилирования кода и глубокого понимания архитектуры платформы. Разработчики ITSTM проведут рефакторинг ваших модулей обмена (REST API, RabbitMQ, HTTP Сервисы), переведут парсинг на потоковые алгоритмы и увеличат скорость интеграции в десятки раз.
Частые вопросы (FAQ)
Можно ли потоково записать структуру или массив целиком?
Да. Можно открыть потоковую 'ЗаписьJSON', записать в нее начало массива, а затем внутри цикла передавать в метод 'ЗаписатьJSON(ЗаписьJSON, МояСтруктураСтроки)' небольшие структуры (например, отдельный товар). 1С сама сериализует структуру и добавит ее в поток. Это золотая середина между простотой кода и экономией ОЗУ.
Как отформатировать JSON 'красиво' (Pretty Print)?
Используйте объект 'ПараметрыЗаписиJSON(ПереносСтрокJSON.Авто, Символ(9))' (Символ(9) — это табуляция) и передайте его в метод ОткрытьФайл(). Но учтите, что форматирование увеличивает размер итогового файла на 10-20% из-за пробелов и переносов.
Поддерживает ли 1С потоковую работу с XML?
Да, аналогичным образом через объекты ЧтениеXML и ЗаписьXML (последовательное чтение узлов). Они работают по тем же SAX/Stream принципам.
Что делать, если JSON приходит не в виде файла, а в виде HTTP-ответа (строки)?
Использовать метод 'УстановитьСтроку()' для объекта ЧтениеJSON (вместо ОткрытьФайл). Но если строка уже в памяти (как тело HTTP ответа), проблема переполнения ОЗУ уже случилась на этапе загрузки HTTP-ответа. Нужно скачивать HTTP-ответ сразу в файл (используя параметр 'ИмяВыходногоФайла' метода ОтправитьДляОбработки).