Jackson使用@JsonSubTypes注解实现多态解析
简介
之前我们都会使用 @JsonDeserialize 与 @JsonSerialize 来指定序列化与反序列化时使用的实际类型,但是现在这里有另外一种情况:
一个父类,多个子类,序列化时要求序列化所有属性,反序列化时要求使用实际类型,显然普通的序列化中没有包含子类类型信息,导致反序列化时无法确定使用多个子类中的哪一个,所以我们借助 Jackson 的 @JsonSubTypes 来实现多态解析。
举例
假如有以下两种格式json:
JSON1.json1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| [ { "id":1, "type":"GITHUB", "name":"Github代码库", "path":"XuxuGood/jackjson", "description":"JackJson多态解析" }, { "id":2, "type":"GITLAB", "name":"Gitlab代码库", "path":"XuxuGood/jackjson", "description":"JackJson多态解析" } ]
|
JSON2.json1 2 3 4 5 6 7
| { "id":1, "type":"GITHUB", "name":"Github代码库", "path":"XuxuGood/jackjson", "description":"JackJson多态解析" }
|
特点:
- 有公共字段,这里是
id 和 type type 用来确定是哪个子类
实现方式一
定义实体类
BaseCodeRepo1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import com.example.jackson.common.RepoType; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo;
import lombok.Data;
@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;
}
|
GithubRepo1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import lombok.Data; import lombok.EqualsAndHashCode;
@Data @EqualsAndHashCode(callSuper = true) public class GithubRepo extends BaseCodeRepo {
private String name;
private String path;
private String description;
}
|
GitlabRepo1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import lombok.Data; import lombok.EqualsAndHashCode;
@Data @EqualsAndHashCode(callSuper = true) public class GitlabRepo extends BaseCodeRepo {
private String name;
private String path;
private String description;
}
|
解析
多个(GithubRepo和GitlabRepo数组)解析:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @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 解析:
1 2 3 4 5 6 7 8 9 10 11 12 13
| @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 剔除掉。
定义实体类
BaseCodeRepo1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import com.example.jackson.common.RepoType; import com.fasterxml.jackson.annotation.JsonTypeInfo;
import lombok.Data;
@Data @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type", visible = true) public class BaseCodeRepo {
private Long id;
private RepoType type;
}
|
GithubRepo1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString;
@Data @JsonTypeName(value = "GITHUB") @EqualsAndHashCode(callSuper = true) public class GithubRepo extends BaseCodeRepo {
private String name;
private String path;
private String description;
}
|
GitlabRepo1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import com.fasterxml.jackson.annotation.JsonTypeName;
import java.util.List;
import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString;
@Data @JsonTypeName(value = "GITLAB") @EqualsAndHashCode(callSuper = true) public class GitlabRepo extends BaseCodeRepo {
private String name;
private String path;
private String description;
}
|
解析
多个(GithubRepo和GitlabRepo数组)解析:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @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 解析:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @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(); } }
|
源码地址