> Как лучше обработать ошибки парсинга JSON в Java (Java)

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

Компании: ИТ-Экспертиза

Стек: Java

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

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

На уровне senior нужно не просто ловить JsonProcessingException, а разделять синтаксические ошибки, несоответствие схеме и бизнес-валидацию. Используй Jackson с ObjectMapper, включай FAIL_ON_UNKNOWN_PROPERTIES по ситуации, оборачивай в доменную ошибку и возвращай клиенту 400 без утечки внутренних деталей. Логируй с контекстом, но без чувствительных данных.

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

Парсинг JSON в Java почти всегда идёт через Jackson, Gson или JSON-B. Синтаксически некорректный JSON выбрасывает JsonParseException или JsonProcessingException - это ошибка клиента, а не сервера. Несоответствие структуры (например, строка вместо числа) даёт MismatchedInputException. Неизвестные поля контролируются DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES - на senior-уровне это осознанный trade-off между строгостью контракта и обратной совместимостью. Бизнес-правила (например, отрицательная сумма) проверяются отдельно через Bean Validation или ручную валидацию после десериализации. Ошибки нужно преобразовывать в доменные исключения, чтобы не протекали детали библиотеки. В Spring MVC HttpMessageNotReadableException уже обрабатывается, но кастомный @ExceptionHandler даёт контроль над телом ответа. Логирование должно включать correlation id, endpoint и фрагмент payload без PII. Для streaming-парсинга (JsonParser) ошибки возникают на этапе итерации, и их тоже нужно оборачивать.

На практике

В REST-контроллере создай @RestControllerAdvice, который ловит HttpMessageNotReadableException и возвращает ProblemDetail с кодом INVALID_JSON. Внутри сервиса, если парсишь вручную, используй ObjectMapper.readValue в try-catch и бросай InvalidPayloadException с причиной. Настрой ObjectMapper как bean: отключи FAIL_ON_UNKNOWN_PROPERTIES для публичных API, включи FAIL_ON_NULL_FOR_PRIMITIVES. Для Kafka-консьюмеров ошибки парсинга отправляй в DLQ, а не ретрай бесконечно. Не логируй весь payload на уровне ERROR - это риск утечки и раздувания логов. Используй StructuredArguments или MDC для контекста.

Пример кода

JAVA
public record OrderRequest(String id, BigDecimal amount) {}
@RestControllerAdvice
public class JsonErrorHandler {
@ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseEntity<ProblemDetail> handleInvalidJson(HttpMessageNotReadableException ex) {
Throwable cause = ex.getMostSpecificCause();
String detail = cause instanceof JsonProcessingException
? "Malformed JSON payload"
: "Invalid request body";
ProblemDetail problem = ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, detail);
problem.setTitle("Invalid JSON");
return ResponseEntity.badRequest().body(problem);
}
}
@Service
public class OrderParser {
private final ObjectMapper mapper;
public OrderParser(ObjectMapper mapper) {
this.mapper = mapper;
}
public OrderRequest parse(String raw) {
try {
OrderRequest request = mapper.readValue(raw, OrderRequest.class);
validate(request);
return request;
} catch (JsonProcessingException e) {
throw new InvalidPayloadException("Cannot parse order JSON", e);
}
}
private void validate(OrderRequest request) {
if (request.amount() == null || request.amount().signum() < 0) {
throw new InvalidPayloadException("Amount must be non-negative");
}
}
}

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

Скажи, что на senior-уровне важно разделять типы ошибок: синтаксис, схема, бизнес-валидация. Упомяни Jackson и его feature-флаги. Покажи, что знаешь про HttpMessageNotReadableException в Spring и кастомный @ExceptionHandler. Расскажи про DLQ для асинхронных сценариев и про то, что нельзя светить stack trace клиенту. Добавь про логирование с контекстом и без PII. Если спросят про производительность, отметь streaming API и переиспользование ObjectMapper.

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

Понимание различий между типами ошибок парсинга. Знание Jackson и его конфигурации. Умение обрабатывать исключения в Spring без утечки деталей. Осознание trade-off между строгостью схемы и обратной совместимостью. Навык логирования и работы с DLQ. Способность предложить доменную модель ошибок вместо голых исключений библиотеки.

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

Ловить общий Exception вместо конкретных типов. Возвращать клиенту stack trace или сообщение Jackson. Логировать весь payload с персональными данными. Игнорировать FAIL_ON_UNKNOWN_PROPERTIES и ломать контракт при добавлении поля. Смешивать синтаксическую и бизнес-валидацию в одном catch. Не обрабатывать ошибки в Kafka-консьюмере и зацикливать ретраи. Создавать новый ObjectMapper на каждый запрос.

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

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