При работе с большими объемами данных в формате JSON программисты 1С часто сталкиваются с проблемой производительности. Стандартный метод ЗаписатьJSON полностью перезаписывает файл. Если ваш файл весит десятки или сотни мегабайт, простая операция добавления одного объекта превращается в ресурсоемкую задачу по чтению и повторной записи всего массива. В этой статье мы разберем, как можно "обмануть" структуру JSON и дописать данные в конец массива, используя низкоуровневую работу с файлами.
JSON-файл, представляющий собой массив объектов, всегда заканчивается закрывающей квадратной скобкой ]. Чтобы добавить новый элемент в этот массив, мы не можем просто дописать строку в конец файла, так как это нарушит синтаксис (данные окажутся за пределами массива). Традиционный подход "прочитать все в память — добавить элемент — записать обратно" быстро приводит к ошибкам "Недостаточно памяти". Поэтому нам необходимо рассмотреть методы прямой манипуляции файловым указателем.
Для решения задачи мы воспользуемся объектом ФайловыйПоток. Суть метода заключается в том, чтобы открыть файл в режиме дозаписи, переместить указатель на один или несколько байтов назад (чтобы "стереть" закрывающую скобку) и записать новые данные, начиная с запятой.
Рассмотрим пример кода, который реализует этот подход:
&НаСервере
Процедура ДозаписатьВJSONНаСервере(ПутьКФайлу, НовыеДанныеСтруктура)
// Подготовим текст для записи: запятая, сам объект и закрывающая скобка массива
ЗаписьJSON = Новый ЗаписьJSON;
ПараметрыЗаписиJSON = Новый ПараметрыЗаписиJSON(, Символы.Таб);
ЗаписьJSON.УстановитьСтроку(ПараметрыЗаписиJSON);
ЗаписатьJSON(ЗаписьJSON, НовыеДанныеСтруктура);
// Убираем возможные лишние символы и подготавливаем строку вставки
СтрокаОбъекта = ЗаписьJSON.Закрыть();
ТекстДляВставки = "," + СтрокаОбъекта + "]";
// Преобразуем строку в буфер двоичных данных (важно соблюдать кодировку UTF-8)
Буфер = ПолучитьБуферДвоичныхДанныхИзСтроки(ТекстДляВставки, КодировкаТекста.UTF8);
// Открываем файл для дозаписи
ФП = Новый ФайловыйПоток(ПутьКФайлу, РежимОткрытияФайла.Дописать);
// Сдвигаем указатель назад на 1 байт, чтобы встать на позицию символа "]"
// ВНИМАНИЕ: это сработает, если файл не имеет переноса строки в конце!
ФП.Перейти(-1, ПозицияВПотоке.Конец);
// Записываем наш буфер
ФП.Записать(Буфер, 0, Буфер.Размер);
ФП.Закрыть();
КонецПроцедуры
Проанализируем ситуацию глубже. Метод с переходом на -1 байт крайне чувствителен к тому, как был сохранен исходный файл. Выясним основные риски:
\r\n автоматически), то последний байт будет не ], а 0x0A. В этом случае переход на -1 байт приведет к повреждению файла. Профессиональный подход требует проверки последних байтов файла перед записью.UTF-8 в начале файла могут стоять три байта EF BB BF (Byte Order Mark). Хотя при дозаписи в конец это не мешает, важно, чтобы кодировка дописываемого фрагмента (через ПолучитьБуферДвоичныхДанныхИзСтроки) строго совпадала с кодировкой файла.[], логика с запятой , перед первым элементом будет ошибочной. Нужно проверять, пуст ли массив, перед вставкой.Если ваша задача — ведение логов или накопление данных, формат "массив объектов" может быть не лучшим выбором. Рассмотрим альтернативу — JSON Lines. В этом формате каждый объект JSON записывается на новой строке без общих квадратных скобок и запятых.
Преимущество этого метода в том, что для добавления данных нам достаточно использовать обычный объект ЗаписьТекста в режиме Дописывать = Истина.
Процедура ДобавитьЛогJSON(ПутьКФайлу, Данные)
Запись = Новый ЗаписьТекста(ПутьКФайлу, КодировкаТекста.UTF8, , Истина); // Истина - режим дозаписи
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, Данные);
СтрокаОбъекта = ЗаписьJSON.Закрыть();
Запись.ЗаписатьСтроку(СтрокаОбъекта);
Запись.Закрыть();
КонецПроцедуры
Для чтения такого файла в 1С мы используем построчное чтение:
Текст = Новый ЧтениеТекста(ПутьКФайлу);
Строка = Текст.ПрочитатьСтроку();
Пока Строка <> Неопределено Цикл
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(Строка);
Объект = ПрочитатьJSON(ЧтениеJSON);
// Обработка объекта...
Строка = Текст.ПрочитатьСтроку();
КонецЦикла;
Если вам все же нужно поддерживать валидный массив JSON и вы боитесь напрямую манипулировать байтами, проанализируем "безопасный" потоковый метод. Вместо загрузки всего файла в память, мы можем создать новый временный файл, перекачать в него данные из старого (кроме последнего символа) и добавить новые.
Разберем по шагам этот алгоритм:
ЧтениеТекста для исходного файла.ЗаписьТекста для нового временного файла.]., и новые объекты.] и закрываем файлы.Этот метод потребляет минимум памяти (только на размер буфера чтения) и гарантирует целостность данных даже при аварийном завершении работы системы.
Мы рассмотрели несколько способов решения задачи. Выбор зависит от ваших требований к надежности и производительности:
ФайловыйПоток с переходом на -1 байт, но обязательно добавьте проверку на наличие символов переноса строки (CR/LF) в конце файла.Помните, что любая ручная манипуляция байтами в JSON требует осторожности. Всегда проверяйте результат в валидаторе после внесения изменений в код обработки.