> Как пропускать некорректный JSON и продолжать обработку (C#)
Уровень: senior · Роль: backend · Язык: C# · Категория: Технические вопросы
Компании: Ozon
Стек: C# /.NET
> Пример ответа
Короткий ответ
Используйте JsonDocument.Parse или JsonSerializer.Deserialize в блоке try-catch для перехвата JsonException. При потоковой обработке (NDJSON, массив объектов) читайте по одному элементу и пропускайте некорректные, логируя ошибку. Для больших объёмов применяйте Utf8JsonReader с ручным управлением позицией. Ключевой trade-off: строгий парсинг всего документа против потокового с восстановлением после ошибки.
Подробное объяснение
В .NET есть несколько уровней работы с JSON, и стратегия пропуска зависит от формата входных данных. Если это единый документ, то при ошибке парсинга восстановиться нельзя - придётся отбросить весь документ. Если это поток (например, построчный JSON или массив), можно изолировать каждый элемент и обрабатывать независимо.
Для построчного формата (NDJSON, JSON Lines) достаточно читать строку за строкой, парсить каждую отдельно и при JsonException логировать и продолжать. Это самый простой и надёжный подход.
Для JSON-массива большого размера JsonSerializer.Deserialize<List<T>> загрузит всё в память и упадёт на первой ошибке. Вместо этого используйте JsonDocument.Parse с JsonReaderOptions или Utf8JsonReader в цикле, переходя от элемента к элементу. При ошибке внутри элемента нужно вручную пропустить его токены до следующего на том же уровне вложенности.
JsonSerializerOptions с AllowTrailingCommas и ReadCommentHandling решают только синтаксические мелочи, но не спасают от структурно некорректного JSON. Для устойчивости к частичным ошибкам нужен именно потоковый подход с обработкой исключений на уровне элемента.
На практике
В реальных backend-системах часто приходят логи, события или сообщения из внешних источников, где часть записей битая. Типичные сценарии: ingestion pipeline, ETL, обработка очередей. Рекомендуется:
- Валидировать каждый элемент отдельно, не полагаясь на валидность всего пакета.
- Логировать сырую строку и позицию ошибки для последующего разбора.
- Использовать
JsonException.PathиLineNumberдля диагностики. - Для высокой нагрузки применять
System.Text.JsonсUtf8JsonReaderиPipeReader, чтобы не аллоцировать лишние строки. - Накапливать счётчики успешных и пропущенных записей для мониторинга.
Пример кода
CSHARPusing System.Text.Json;public static IEnumerable<T> ParseSkippingInvalid<T>(IEnumerable<string> lines, ILogger logger){foreach (var line in lines){T? item = default;try{item = JsonSerializer.Deserialize<T>(line);}catch (JsonException ex){logger.LogWarning(ex, "Пропущена некорректная строка: {Line}", line);continue;}if (item is not null)yield return item;}}
Для массива с потоковым чтением:
CSHARPpublic static async IAsyncEnumerable<T> ReadArraySkippingInvalid<T>(Stream stream){var options = new JsonReaderOptions { AllowTrailingCommas = true };using var reader = new Utf8JsonReader(await ReadAllBytesAsync(stream), options);if (!reader.Read() || reader.TokenType != JsonTokenType.StartArray)yield break;while (reader.Read() && reader.TokenType != JsonTokenType.EndArray){T? item = default;try{item = JsonSerializer.Deserialize<T>(ref reader);}catch (JsonException){// Пропускаем текущий элемент до следующего на том же уровнеreader.Skip();continue;}if (item is not null)yield return item;}}
Как отвечать на собеседовании
Начните с уточнения формата: единый документ или поток. Объясните, что для потока нужно изолировать элементы и обрабатывать исключения на каждом. Упомяните JsonException, Utf8JsonReader и Skip(). Покажите понимание trade-off между строгостью и устойчивостью. Приведите пример с логированием и счётчиками. Если спросят про производительность, расскажите про PipeReader и избегание лишних аллокаций.
Что проверяет интервьюер
Понимание различий между парсингом целого документа и потоковой обработкой. Знание API System.Text.Json: JsonDocument, Utf8JsonReader, JsonSerializer. Умение обрабатывать исключения без падения всего pipeline. Осознание trade-off между строгостью валидации и отказоустойчивостью. Навык логирования и мониторинга ошибок. Опыт работы с большими объёмами данных и потоковыми форматами.
Типичные ошибки
Использование JsonSerializer.Deserialize<List<T>> для больших массивов с надеждой пропустить битые элементы - при первой ошибке падает весь вызов. Игнорирование JsonException.Path и LineNumber, что затрудняет диагностику. Попытка восстановить позицию в Utf8JsonReader без Skip(), что приводит к рассинхронизации. Логирование без ограничения размера, что забивает диски. Отсутствие счётчиков пропущенных записей, из-за чего проблемы остаются незамеченными. Смешивание валидации схемы и синтаксического парсинга в одном слое.
> Похожие задачи по C#
Как реализовать чтение JSON по строкам с использованием потоков
Как обрабатывать отсутствие одинаковых строк в JSON
Как реализовать обработку больших JSON файлов, которые не помещаются в память
Что такое table expressions в SQL?
> Похожие задачи по backend
Как реализовать чтение JSON по строкам с использованием потоков
Как обрабатывать отсутствие одинаковых строк в JSON
Как реализовать обработку больших JSON файлов, которые не помещаются в память
Что такое table expressions в SQL?
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью