JSON泛型集合反序列化

时间:2016-09-01 14:16:50

标签: java json spring deserialization generic-collections

我用Java编写了这样的DTO类:

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(注释)类,以便能够在其正文中使用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"
            }
        }
    ]
}

不幸的是,默认情况下,杰克逊将AnswerDto对象的值映射到LinkedHashMap而不是正确的(LocationJobTitle)类类型的对象。 我应该使用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>>() {
});

没有任何其他自定义配置。

2 个答案:

答案 0 :(得分:5)

我已使用杰克逊的自定义注释@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;
}

答案 1 :(得分:1)

我的建议是为可能的答案值创建一个单独的界面,并在其上使用@JsonTypeInfo。您也可以从public class Download : IHttpHandler { public void ProcessRequest(HttpContext context) { string file = "setup.exe"; if (File.Exists(context.Server.MapPath(file)) && Session["scr"].ToString() == "Ok") { context.Response.Clear(); context.Response.ContentType = "application/octet-stream"; context.Response.AddHeader("content-disposition", "attachment;filename=" + Path.GetFileName(file)); context.Response.WriteFile(context.Server.MapPath(file)); context.Response.End(); } else { context.Response.ContentType = "text/plain"; context.Response.Write("File cannot be found!"); } } public bool IsReusable { get { return false; } } } type枚举中删除AnswerDto字段,然后通过其他AnswerType类杰克逊为您添加类型信息。喜欢这个

*AnswerDto

结果json将如下所示

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 { /*..*/ }

将使用

解析
{
  "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"
      }
    }
  ]
}

但是这个解决方案仅适用于您是json数据生产者的情况,而且您不必考虑向后兼容性。

在其他情况下,您必须为AnswersDto answersDto = objectMapper.readValue(json, AnswersDto.class); 类制作自定义desetializer。