In Part 1, we covered some simple parsing examples:

  • 一个简单的JSON请求主体,带有两个字符串成员,Spring Boot / Jackson将它们解析为可变的Java对象将相同的简单JSON请求主体解析为不可变的Java对象具有一个字符串成员和一个对象成员的更复杂的JSON请求主体在前面的要点中提到的更复杂的对象,其中添加了一个值数组表示对象数组的JSON请求正文 A “wrapped” JSON object

在下一部分中,我们将介绍您可能会遇到的四个新用例:

  • 处理日期处理枚举忽略我们不在乎的字段明智地默认为空值

Handling Dates

Continuing on with the example from Part 1, let’s consider if our JSON representation of a smartphone had just a description (like “Samsung Galaxy S20” or “Apple iPhone 11”) and a release date. The Samsung Galaxy S20 might look like this:



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



The standard Java class for a date is LocalDate and Jackson, the JSON parsing/generating library that Spring Boot uses, can parse JSON to a LocalDate. So the POJO for the above JSON would look like this:




Notice that the only annotation we use here is “@JsonFormat” where we supply a “pattern.” This pattern must be a Java SimpleDateFormat pattern string. Without the @JsonFormat annotation and the pattern string, you would have to submit only request bodies matching the default format of “yyyy-MM-d” (such as “2019-09-20”).

我认为,在POJO中使用LocalDate类型时,无论是否接受默认值,都应始终使用@JsonFormat批注,以明确告知代码的未来维护者期望的模式。

通过该POJO,可以使用以下SmartphoneController.java:




重建并重新启动应用程序并进行测试。 您应该能够成功发布上面给出的请求,并且您将看到类似以下的日志记录:



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

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"
}

{
    "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 Bad Request进行响应。

Handling Enumerations

如果您要求API的客户端从一组特定的常量中发布数据,则通常使用Java枚举。 例如,考虑是否每个电话都标记有生命周期状态,该状态是RELEASE_ANNOUNCED,RELEASE_DELAYED,ACTIVE或RETIRED之一。

JSON可能类似于以下内容。



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



将生命周期视为字符串当然是可能的,但是由于您知道该值应该是固定的一组常量之一,因此可以通过将其作为枚举来获得很多好处。 这是一个以生命周期为枚举的Smartphone.java版本:




完成此操作后,您无需对SmartphoneController.java进行任何更改。 重建并重新启动应用程序,然后试一试。

您还将注意到,如果您尝试发布无效的生命周期值,则会收到400 Bad Request响应。



{
    "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"
}

{
    "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"
}



Ignoring fields the application doesn’t care about

考虑一个想法,即客户端应用程序添加了一个新字段,而该应用程序不知道(或关心)该字段。 从前面的示例中获取代码,我们现在可以接收到这样的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将Jackson配置为自动忽略应用程序不知道的字段。

Custom null value handling

同样,null完全可以接受。 例如,再次使用现有代码,想象是否有人没有提供期望值(例如releaseDate),然后发送以下代码:



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



在这种情况下,应用程序只是为releaseDate成员选择了“ null”值。 您可以在应用程序日志中看到此内容,该日志将显示:



The Apple iPhone 11 was released on null

The Apple iPhone 11 was released on null



这可能是不希望的。 如果您希望始终以发送方式接收发送给您的请求,则这很有意义。 以类似的方式,如果您期望布尔值,整数或长整数之类的原始值且未提供,则将选择默认值(例如,整数将默认为0,布尔值将为false等等)。

但是,如果您要更改此设置,则可以。 只需添加@JsonProperty使用该字段注释必需=真旗。




现在,每当您发送不带有releaseDate的请求时,它将收到400 Bad Request响应。

Conclusion

As I mentioned in Part 1, I hope this walkthrough of common Spring Boot JSON parsing use cases was useful. Did I miss any? Was anything unclear? Any other comments or questions? Let me know in the comment section below.


from: https://dev.to//scottshipp/parsing-json-in-spring-boot-part-2-2206