发布于 

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

简介

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

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

举例

假如有以下两种格式json:

JSON1.json
1
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.json
1
2
3
4
5
6
7
{
"id":1,
"type":"GITHUB",
"name":"Github代码库",
"path":"XuxuGood/jackjson",
"description":"JackJson多态解析"
}

特点:

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

实现方式一

定义实体类

BaseCodeRepo
1
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;

/**
* @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;

}
GithubRepo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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;

}
GitlabRepo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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数组)解析:

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 剔除掉。

定义实体类

BaseCodeRepo
1
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;

/**
* @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;

}
GithubRepo
1
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;

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

private String name;

private String path;

private String description;

}
GitlabRepo
1
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;

/**
* @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数组)解析:

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();
}
}

源码地址


本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

本站由 @银河小徐 创建,使用 Stellar 作为主题。

本站由又拍云提供加速。

京ICP备2020040230号。

载入天数...载入时分秒...访客数: 人,总访问量: 次。