> Как реализовать чтение JSON по строкам с использованием потоков (C#)

Уровень: senior · Роль: backend · Язык: C# · Категория: Технические вопросы

Компании: Ozon

Стек: C# /.NET

> Пример ответа

Короткий ответ

Для чтения JSON по строкам в C#/.NET используйте System.Text.Json с JsonSerializer.DeserializeAsyncEnumerable<T> или JsonDocument/Utf8JsonReader в потоковом режиме. Для построчного чтения NDJSON применяйте StreamReader.ReadLineAsync и десериализуйте каждую строку отдельно. Это позволяет обрабатывать большие файлы без загрузки всего содержимого в память, что критично для backend-сервисов с ограниченными ресурсами.

Подробное объяснение

Потоковое чтение JSON бывает двух основных сценариев: NDJSON (newline-delimited JSON), где каждый объект на отдельной строке, и большой единый JSON-массив. В первом случае достаточно StreamReader.ReadLineAsync в цикле с JsonSerializer.Deserialize<T>. Во втором - JsonSerializer.DeserializeAsyncEnumerable<T> из .NET 6+ возвращает IAsyncEnumerable<T>, читая элементы массива по мере поступления. Альтернатива - Utf8JsonReader для ручного парсинга без аллокаций, но он сложнее и требует ручного управления состоянием. Для очень больших файлов комбинируйте FileStream с FileOptions.SequentialScan и буферизацией. Важно учитывать backpressure: IAsyncEnumerable с WithCancellation и ConfigureAwait(false) предотвращает блокировки. На senior-уровне ожидается понимание trade-off между аллокациями, пропускной способностью и сложностью кода.

На практике

В микросервисах часто приходят NDJSON-логи или стриминговые ответы API. Читайте через HttpClient.GetStreamAsync и ReadLineAsync, десериализуя построчно. Для единого массива используйте DeserializeAsyncEnumerable с JsonSerializerOptions и CancellationToken. Не забывайте про JsonSerializerOptions.PropertyNameCaseInsensitive и обработку JsonException для битых строк. При работе с Utf8JsonReader применяйте ReadOnlySequence<byte> и SequenceReader для эффективного парсинга без копирования. В high-load сценариях рассмотрите System.IO.Pipelines для управления буферами.

Пример кода

CSHARP
// NDJSON: построчное чтение
await using var stream = File.OpenRead("data.ndjson");
using var reader = new StreamReader(stream);
while (!reader.EndOfStream)
{
var line = await reader.ReadLineAsync();
if (string.IsNullOrWhiteSpace(line)) continue;
var item = JsonSerializer.Deserialize<MyDto>(line);
// обработка item
}
// JSON-массив: потоковая десериализация
await using var fs = File.OpenRead("array.json");
await foreach (var item in JsonSerializer.DeserializeAsyncEnumerable<MyDto>(fs))
{
// обработка item
}

Как отвечать на собеседовании

Начните с уточнения формата: NDJSON или единый массив. Затем предложите DeserializeAsyncEnumerable для массива и ReadLineAsync для NDJSON. Упомяните Utf8JsonReader как low-level опцию с минимальными аллокациями. Объясните, почему не стоит использовать JsonDocument.Parse для больших файлов - он загружает всё в память. Добавьте про CancellationToken и ConfigureAwait(false) для асинхронной обработки. Если спросят про производительность, расскажите о System.IO.Pipelines и ArrayPool<byte>.

Что проверяет интервьюер

Понимание потоковой обработки и управления памятью. Знание System.Text.Json и его асинхронных API. Умение выбирать между JsonDocument, Utf8JsonReader и DeserializeAsyncEnumerable. Опыт работы с IAsyncEnumerable и отменой операций. Способность обсуждать trade-off между простотой и производительностью. Навык обработки ошибок и частичных данных в потоке.

Типичные ошибки

Использование JsonDocument.Parse для больших файлов, что приводит к OOM. Игнорирование CancellationToken и ConfigureAwait(false). Чтение всего потока в строку перед парсингом. Отсутствие обработки JsonException для отдельных строк в NDJSON. Забывание про FileOptions.SequentialScan для больших файлов. Синхронные вызовы ReadLine в асинхронном контексте. Неправильная работа с Utf8JsonReader без учёта IsFinalBlock и TokenType.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью