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