JSON 通用集合反序列化
JSON generic collection deserialization
我有这样的 DTO classes 写在 Java:
public class AnswersDto {
private String uuid;
private Set<AnswerDto> answers;
}
public class AnswerDto<T> {
private String uuid;
private AnswerType type;
private T value;
}
class LocationAnswerDto extends AnswerDto<Location> {
}
class JobTitleAnswerDto extends AnswerDto<JobTitle> {
}
public enum AnswerType {
LOCATION,
JOB_TITLE,
}
class Location {
String text;
String placeId;
}
class JobTitle {
String id;
String name;
}
在我的项目中有 Jackson 库用于 JSON 的序列化和反序列化。
如何配置 AnswersDto
(使用特殊注释)或 AnswerDto
(也使用注释)classes 以便能够正确反序列化其中包含 AnswersDto
的请求正文,例如:
{
"uuid": "e82544ac-1cc7-4dbb-bd1d-bdbfe33dee73",
"answers": [
{
"uuid": "e82544ac-1cc7-4dbb-bd1d-bdbfe33dee73",
"type": "LOCATION",
"value": {
"text": "Dublin",
"placeId": "121"
}
},
{
"uuid": "e82544ac-1cc7-4dbb-bd1d-bdbfe33dee73",
"type": "JOB_TITLE",
"value": {
"id": "1",
"name": "Developer"
}
}
]
}
不幸的是,Jackson 默认将 AnswerDto
对象的值映射到 LinkedHashMap
而不是正确(Location
或 JobTitle
)class 类型的对象。
我应该编写自定义 JsonDeserializer<AnswerDto>
还是使用 @JsonTypeInfo
和 @JsonSubTypes
进行配置就足够了?
只用一个 AnswerDto
以
的形式正确反序列化请求
{
"uuid": "e82544ac-1cc7-4dbb-bd1d-bdbfe33dee73",
"type": "LOCATION",
"value": {
"text": "Dublin",
"placeId": "121"
}
}
我正在使用:
AnswerDto<Location> answerDto = objectMapper.readValue(jsonRequest, new TypeReference<AnswerDto<Location>>() {
});
没有任何其他自定义配置。
我的建议是为可能的答案值制作一个单独的界面,并在其上使用 @JsonTypeInfo
。您还可以从 AnswerDto
、AnswerType
枚举和其他 *AnswerDto
classes 中删除 type
字段,因为 jackson 会为您添加类型信息。像这样
public class AnswerDto<T extends AnswerValue> {
private String uuid;
private T value;
}
@JsonTypeInfo(use = Id.CLASS, include = As.PROPERTY)
interface AnswerValue {}
class Location implements AnswerValue { /*..*/ }
class JobTitle implements AnswerValue { /*..*/ }
结果 json 将如下所示
{
"uuid": "e82544ac-1cc7-4dbb-bd1d-bdbfe33dee73",
"answers": [
{
"uuid": "e82544ac-1cc7-4dbb-bd1d-bdbfe33dee73",
"value": {
"@class": "com.demo.Location",
"text": "Dublin",
"placeId": "121"
}
},
{
"uuid": "e82544ac-1cc7-4dbb-bd1d-bdbfe33dee73",
"value": {
"@class": "com.demo.JobTitle",
"id": "1",
"name": "Developer"
}
}
]
}
将使用
解析
AnswersDto answersDto = objectMapper.readValue(json, AnswersDto.class);
但此解决方案仅适用于您是 json 数据的生产者并且您不必考虑向后兼容性的情况。
在其他情况下,您必须为 AnswersDto
class.
制作自定义 desetializer
我已经使用 Jackson 的自定义注释解决了问题 @JsonTypeInfo
和 @JsonSubTypes
:
public class AnswerDto<T> {
private String uuid;
private AnswerType type;
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = Location.class, name = AnswerType.Types.LOCATION),
@JsonSubTypes.Type(value = JobTitle.class, name = AnswerType.Types.JOB_TITLE)
})
private T value;
}
我有这样的 DTO classes 写在 Java:
public class AnswersDto {
private String uuid;
private Set<AnswerDto> answers;
}
public class AnswerDto<T> {
private String uuid;
private AnswerType type;
private T value;
}
class LocationAnswerDto extends AnswerDto<Location> {
}
class JobTitleAnswerDto extends AnswerDto<JobTitle> {
}
public enum AnswerType {
LOCATION,
JOB_TITLE,
}
class Location {
String text;
String placeId;
}
class JobTitle {
String id;
String name;
}
在我的项目中有 Jackson 库用于 JSON 的序列化和反序列化。
如何配置 AnswersDto
(使用特殊注释)或 AnswerDto
(也使用注释)classes 以便能够正确反序列化其中包含 AnswersDto
的请求正文,例如:
{
"uuid": "e82544ac-1cc7-4dbb-bd1d-bdbfe33dee73",
"answers": [
{
"uuid": "e82544ac-1cc7-4dbb-bd1d-bdbfe33dee73",
"type": "LOCATION",
"value": {
"text": "Dublin",
"placeId": "121"
}
},
{
"uuid": "e82544ac-1cc7-4dbb-bd1d-bdbfe33dee73",
"type": "JOB_TITLE",
"value": {
"id": "1",
"name": "Developer"
}
}
]
}
不幸的是,Jackson 默认将 AnswerDto
对象的值映射到 LinkedHashMap
而不是正确(Location
或 JobTitle
)class 类型的对象。
我应该编写自定义 JsonDeserializer<AnswerDto>
还是使用 @JsonTypeInfo
和 @JsonSubTypes
进行配置就足够了?
只用一个 AnswerDto
以
{
"uuid": "e82544ac-1cc7-4dbb-bd1d-bdbfe33dee73",
"type": "LOCATION",
"value": {
"text": "Dublin",
"placeId": "121"
}
}
我正在使用:
AnswerDto<Location> answerDto = objectMapper.readValue(jsonRequest, new TypeReference<AnswerDto<Location>>() {
});
没有任何其他自定义配置。
我的建议是为可能的答案值制作一个单独的界面,并在其上使用 @JsonTypeInfo
。您还可以从 AnswerDto
、AnswerType
枚举和其他 *AnswerDto
classes 中删除 type
字段,因为 jackson 会为您添加类型信息。像这样
public class AnswerDto<T extends AnswerValue> {
private String uuid;
private T value;
}
@JsonTypeInfo(use = Id.CLASS, include = As.PROPERTY)
interface AnswerValue {}
class Location implements AnswerValue { /*..*/ }
class JobTitle implements AnswerValue { /*..*/ }
结果 json 将如下所示
{
"uuid": "e82544ac-1cc7-4dbb-bd1d-bdbfe33dee73",
"answers": [
{
"uuid": "e82544ac-1cc7-4dbb-bd1d-bdbfe33dee73",
"value": {
"@class": "com.demo.Location",
"text": "Dublin",
"placeId": "121"
}
},
{
"uuid": "e82544ac-1cc7-4dbb-bd1d-bdbfe33dee73",
"value": {
"@class": "com.demo.JobTitle",
"id": "1",
"name": "Developer"
}
}
]
}
将使用
解析AnswersDto answersDto = objectMapper.readValue(json, AnswersDto.class);
但此解决方案仅适用于您是 json 数据的生产者并且您不必考虑向后兼容性的情况。
在其他情况下,您必须为 AnswersDto
class.
我已经使用 Jackson 的自定义注释解决了问题 @JsonTypeInfo
和 @JsonSubTypes
:
public class AnswerDto<T> {
private String uuid;
private AnswerType type;
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = Location.class, name = AnswerType.Types.LOCATION),
@JsonSubTypes.Type(value = JobTitle.class, name = AnswerType.Types.JOB_TITLE)
})
private T value;
}