侧边栏壁纸
博主头像
银河小徐博主等级

A Good Boy ⛵️⛵️⛵️

  • 累计撰写 42 篇文章
  • 累计创建 39 个标签
  • 累计收到 10 条评论

目 录CONTENT

文章目录

Jackson使用@JsonSubTypes注解实现多态解析

银河小徐
2021-08-27 / 0 评论 / 11 点赞 / 81 阅读 / 4,705 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-06-04,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

简介

之前我们都会使用 @JsonDeserialize@JsonSerialize 来指定序列化与反序列化时使用的实际类型,但是现在这里有另外一种情况:

一个父类,多个子类,序列化时要求序列化所有属性,反序列化时要求使用实际类型,显然普通的序列化中没有包含子类类型信息,导致反序列化时无法确定使用多个子类中的哪一个,所以我们借助 Jackson 的 @JsonSubTypes 来实现多态解析。

举例

假如有以下两种格式json:

[
    {
        "id":1,
        "type":"GITHUB",
        "name":"Github代码库",
        "path":"XuxuGood/jackjson",
        "description":"JackJson多态解析"
    },
    {
        "id":2,
        "type":"GITLAB",
        "name":"Gitlab代码库",
        "path":"XuxuGood/jackjson",
        "description":"JackJson多态解析"
    }
]
{
    "id":1,
    "type":"GITHUB",
    "name":"Github代码库",
    "path":"XuxuGood/jackjson",
    "description":"JackJson多态解析"
}

特点:

  1. 有公共字段,这里是 idtype
  2. type 用来确定是哪个子类

实现方式一

定义实体类

import com.example.jackson.common.RepoType;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

import lombok.Data;

/**
 * @author xiaoxuxuy
 */
@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type", visible = true)
@JsonSubTypes({
        @JsonSubTypes.Type(value = GithubRepo.class, name = "GITHUB"),
        @JsonSubTypes.Type(value = GitlabRepo.class, name = "GITLAB")
})
public class BaseCodeRepo {

    private Long id;

    private RepoType type;

}
import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * @author xiaoxuxuy
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class GithubRepo extends BaseCodeRepo {

    private String name;

    private String path;

    private String description;

}
import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * @author xiaoxuxuy
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class GitlabRepo extends BaseCodeRepo {

    private String name;

    private String path;

    private String description;

}

解析

多个(GithubRepo和GitlabRepo数组)解析:

@Test
public void version1Json1Test() {
    String jsonString = JsonConst.JSON1;
    log.info("Json1字符串为: {}", jsonString);

    ObjectMapper objectMapper = new ObjectMapper();
    try {
        List<BaseCodeRepo> baseCodeRepos = objectMapper.readValue(jsonString, new TypeReference<List<BaseCodeRepo>>() {
        });
        baseCodeRepos.forEach(item -> log.info(item.getType() + "对象信息为: {}", JSONObject.toJSONString(item)));
    } catch (IOException e) {
        e.printStackTrace();
    }
}

单个 GithubRepo 解析:

@Test
public void version1Json2Test() {
    String jsonString = JsonConst.JSON2;
    log.info("Json2字符串为: {}", jsonString);

    ObjectMapper mapper = new ObjectMapper();
    try {
        GithubRepo githubRepo = (GithubRepo) mapper.readValue(jsonString, BaseCodeRepo.class);
        log.info("GITHUB对象信息为: {}", JSONObject.toJSONString(githubRepo));
    } catch (IOException e) {
        e.printStackTrace();
    }
}

实现方式二

JsonTypeInfo 借助 @JsonSubTypes 注解来感知抽象类的有哪些实现类,并且知道是如何匹配的。在大型工程中抽象类的子类很多(接口的实现很多),那么 @JsonSubTypes 注解就显得十分臃肿,所以借助以下方式将 @JsonSubTypes 剔除掉。

Readme Card

定义实体类

import com.example.jackson.common.RepoType;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

import lombok.Data;

/**
 * @author xiaoxuxuy
 */
@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type", visible = true)
public class BaseCodeRepo {

    private Long id;

    private RepoType type;

}
import com.fasterxml.jackson.annotation.JsonTypeName;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;

/**
 * @author xiaoxuxuy
 */
@Data
@JsonTypeName(value = "GITHUB")
@EqualsAndHashCode(callSuper = true)
public class GithubRepo extends BaseCodeRepo {

    private String name;

    private String path;

    private String description;

}
import com.fasterxml.jackson.annotation.JsonTypeName;

import java.util.List;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;

/**
 * @author xiaoxuxuy
 */
@Data
@JsonTypeName(value = "GITLAB")
@EqualsAndHashCode(callSuper = true)
public class GitlabRepo extends BaseCodeRepo {

    private String name;

    private String path;

    private String description;

}

解析

多个(GithubRepo和GitlabRepo数组)解析:

@Test
public void version2Json1Test() {
    ObjectMapper mapper = new ObjectMapper();
    // 注入多态解析类
    ClassFactory.BASE_CODE_REPOS.forEach(mapper::registerSubtypes);

    String jsonString = JsonConst.JSON1;
    log.info("Json1字符串为: {}", jsonString);

    try {
        List<BaseCodeRepo> baseCodeRepos = mapper.readValue(jsonString, new TypeReference<List<BaseCodeRepo>>() {
        });
        baseCodeRepos.forEach(item -> log.info(item.getType() + "对象信息为: {}", JSONObject.toJSONString(item)));
    } catch (IOException e) {
        e.printStackTrace();
    }
}

单个 GithubRepo 解析:

@Test
public void version2Json2Test() {
    ObjectMapper mapper = new ObjectMapper();
    // 注入多态解析类
    ClassFactory.BASE_CODE_REPOS.forEach(mapper::registerSubtypes);

    String jsonString = JsonConst.JSON2;
    log.info("Json2字符串为: {}", jsonString);

    try {
        GithubRepo githubRepo = (GithubRepo) mapper.readValue(jsonString, BaseCodeRepo.class);
        log.info("GITHUB对象信息为: {}", JSONObject.toJSONString(githubRepo));
    } catch (IOException e) {
        e.printStackTrace();
    }
}

源码地址

Readme Card

11

评论区