Рубрики
Без рубрики

Синтаксический анализ JSON в Spring Boot, часть 2

В этой второй части вы узнаете, как обрабатывать даты, перечисления, нули и игнорируемые поля. С пометкой java, веб-разработчик, учебник.

В Части 1 мы рассмотрели несколько простых примеров синтаксического анализа:

  • Простое тело запроса JSON с двумя строковыми элементами, которые Spring Boot/Jackson проанализировал как изменяемый объект Java

  • То же самое простое тело запроса JSON, проанализированное как неизменяемый объект Java

  • Более сложное тело запроса JSON, содержащее один элемент строки и один элемент объекта

  • Более сложный объект, упомянутый в предыдущем пункте, с добавлением массива значений

  • Тело запроса JSON, которое представляло массив объектов “Завернутый” объект JSON

В этой следующей части мы рассмотрим четыре новых варианта использования, с которыми вы, вероятно, столкнетесь в какой-то момент:

  • Даты обработки

  • Обработка перечислений

  • Игнорирование полей, о которых мы не заботимся

  • Разумное значение нулевых значений по умолчанию

Продолжая пример из Часть 1 , давайте рассмотрим, было ли в вашем представлении смартфона в формате JSON только описание (например, “Samsung Galaxy S20” или “Apple iPhone 11”) и дата выпуска. Samsung Galaxy S20 может выглядеть так:

{
    "description": "Apple iPhone 11",
    "releaseDate": "September 20, 2019"
}

Стандартный класс Java для даты – Локальная дата и Джексон, библиотека синтаксического анализа/генерации JSON, которую использует Spring Boot, может анализировать JSON с локальной датой. Таким образом, POJO для вышеупомянутого JSON будет выглядеть так:

Обратите внимание, что единственная аннотация, которую мы здесь используем, – это “@JsonFormat”, где мы указываем “шаблон”. Этот шаблон должен быть строкой шаблона Java SimpleDateFormat . Без аннотации @JsonFormat и строки шаблона вам пришлось бы отправлять только тела запросов, соответствующие формату по умолчанию “гггг-ММ-д” (например, “2019-09-20”).

На мой взгляд, при работе с локальным типом данных в вашем POJO вы всегда должны использовать аннотацию @JsonFormat независимо от того, принимаете ли вы значение по умолчанию или нет, просто чтобы быть ясным для будущих разработчиков кода о том, какой шаблон ожидается.

С помощью этого POJO, следующее SmartphoneController.java может быть использован:

Перестройте и перезапустите приложение и протестируйте это. Вы должны быть в состоянии успешно отправить запрос, указанный выше, и вы увидите вход в систему, как:

The Apple iPhone 11 was released on 2019-09-20

Вы заметите, что, поскольку оператор log не форматировал дату, а вызывал метод toString() по умолчанию (по сути), дата была зарегистрирована в вышеупомянутом формате по умолчанию.

Теперь попытайтесь отправить запрос с датой, которая не соответствует этому формату, например:

{
   "description": "Apple iPhone 11",
   "releaseDate": "2019-09-20"
}

Вы получите ответ, подобный:

{
    "timestamp": "2020-04-01T22:05:30.811+0000",
    "status": 400,
    "error": "Bad Request",
    "message": "JSON parse error: Cannot deserialize value of type `java.time.LocalDate` from String \"2019-09-20\": Failed to deserialize java.time.LocalDate: (java.time.format.DateTimeParseException) Text '2019-09-20' could not be parsed at index 0; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.time.LocalDate` from String \"2019-09-20\": Failed to deserialize java.time.LocalDate: (java.time.format.DateTimeParseException) Text '2019-09-20' could not be parsed at index 0\n at [Source: (PushbackInputStream); line: 3, column: 17] (through reference chain: com.scottshipp.code.restservice.datehandling.Smartphone[\"releaseDate\"])",
    "path": "/smartphone"
}

Поэтому приложение применяет формат даты при отправке запроса и отвечает 400 Ошибочным запросом, если формат даты не соответствует шаблону.

Если вы запрашиваете, чтобы клиенты вашего API публиковали данные из определенного набора констант, обычно используется перечисление Java. Например, подумайте, был ли каждый телефон помечен статусом жизненного цикла, который является статусом RELEASE_ANNOUNCED, RELEASE_DELAYED, АКТИВНЫМ или УДАЛЕННЫМ.

JSON для этого может выглядеть следующим образом.

{
    "description": "Apple iPhone 11",
    "releaseDate": "September 20, 2019",
    "lifecycle": "ACTIVE"
}

Конечно, можно рассматривать жизненный цикл как строку, но поскольку вы знаете, что значение должно быть одним из фиксированного набора констант, вы можете получить много преимуществ, сделав это перечислением. Вот версия смартфона.java с жизненным циклом в виде перечисления:

С учетом этого вам не нужно будет вносить какие-либо изменения в SmartphoneController.java . Перестройте и перезапустите приложение и попробуйте его.

Вы также заметите, что если вы попытаетесь опубликовать недопустимое значение для жизненного цикла, вы получите 400 неверных ответов на запрос.

{
    "timestamp": "2020-04-01T22:24:38.026+0000",
    "status": 400,
    "error": "Bad Request",
    "message": "JSON parse error: Cannot deserialize value of type `com.scottshipp.code.restservice.datehandling.Smartphone$Lifecycle` from String \"DEPRECATED\": not one of the values accepted for Enum class: [RETIRED, RELEASE_DELAYED, RELEASE_ANNOUNCED, ACTIVE]; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `com.scottshipp.code.restservice.datehandling.Smartphone$Lifecycle` from String \"DEPRECATED\": not one of the values accepted for Enum class: [RETIRED, RELEASE_DELAYED, RELEASE_ANNOUNCED, ACTIVE]\n at [Source: (PushbackInputStream); line: 4, column: 15] (through reference chain: com.scottshipp.code.restservice.datehandling.Smartphone[\"lifecycle\"])",
    "path": "/smartphone"
}

Рассмотрим идею о том, что клиентское приложение добавляет новое поле без ведома приложения (или заботы) об этом. Взяв код, который у нас есть из предыдущего примера, мы можем прямо сейчас получить тело запроса JSON, подобное этому:

{
    "description": "Apple iPhone 11",
    "releaseDate": "September 20, 2019",
    "lifecycle": "DEPRECATED"
}

Что, если кто-нибудь пришлет это?

{
    "description": "Apple iPhone 11",
    "releaseDate": "September 20, 2019",
    "lifecycle": "ACTIVE",
    "carriers": ["T-Mobile", "Verizon", "AT&T"]
}

Попробуйте и посмотрите, что получится. В текущих версиях Spring Boot это будет работать просто отлично. Spring Boot настраивает Джексона на автоматическое игнорирование полей, о которых приложение не знает.

Аналогичным образом, null является полностью приемлемым. Например, снова используя существующий код, представьте, что кто-то не указывает ожидаемое значение, например дату выпуска, и отправляет это:

{
    "description": "Apple iPhone 11",
    "lifecycle": "ACTIVE"
}

В этом случае приложение просто выбирает значение “null” для элемента Даты выпуска. Вы можете увидеть это в журнале приложений, который покажет:

The Apple iPhone 11 was released on null

Это может быть желательным, а может и не быть. Это имеет смысл, если вы хотите всегда получать отправленный вам запрос так, как он был отправлен. Аналогичным образом, если вы ожидаете примитивное значение, такое как логическое значение, int или long, и оно не указано, вместо этого будет выбрано значение по умолчанию (например, значение int по умолчанию равно 0, логическое значение равно false и так далее).

Но если ты хочешь это изменить, ты можешь. Просто добавьте аннотацию @JsonProperty в поле с флагом обязательно .

Теперь всякий раз, когда вы отправляете запрос без даты выпуска, он получит 400 неверных ответов на запрос.

Как я уже упоминал в Часть 1 , я надеюсь, что это пошаговое руководство по обычным случаям использования JSON-синтаксического анализа Spring Boot было полезным. Я что-нибудь пропустил? Было ли что-нибудь неясно? Есть еще какие-нибудь комментарии или вопросы? Дайте мне знать в разделе комментариев ниже.

Оригинал: “https://dev.to/scottshipp/parsing-json-in-spring-boot-part-2-2206”