Java的Jackson自定义序列化详解
作者:杨某人信了你的邪
Jackson自定义序列化
本篇简单写写Jackson中自定义序列化的方式。
假设有类Country和Leader,分别表示国家以及国家领导人,领导人除了姓名外还有年龄和民族,定义如下
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Country {
private String countryName;
private Leader leader;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Leader {
private String leaderName;
private Integer age;
private String nation;
}
基于上述定义,且看下面的代码输出结果结果
@Test
void test5() throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
Country country = new Country("元朝", new Leader("忽必烈", 79, "蒙古族"));
System.out.println(objectMapper.writeValueAsString(country));
}
输出结果
{"countryName":"元朝","leader":{"leaderName":"忽必烈","age":79,"nation":"蒙古族"}}
可以看到,正常的序列化方式会将leader的属性都进行序列化。
然而,有些时候,我们并不需要关心这个国家的领导人的详细信息,我们只需要知道他叫什么名字即可,例如我们想在序列化之后输入如下的JSON,那就需要定制序列化方式。
1、自定义序列化器
首先定义一个序列化器,继承自Serializer类或其子类
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import java.io.IOException;
public class LeaderSerializer extends StdSerializer<Leader> {
public LeaderSerializer() {
this(null);
}
protected LeaderSerializer(Class<Leader> t) {
super(t);
}
@Override
public void serialize(Leader value, JsonGenerator gen, SerializerProvider provider) throws IOException {
if (value == null) {
gen.writeNull();
return;
}
// 直接输入领导人姓名,不需要起始和结束的花括号
gen.writeString(value.getLeaderName());
}
}
有了序列化,自然需要反序列化
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import java.io.IOException;
public class LeaderDeserializer extends StdDeserializer<Leader> {
public LeaderDeserializer() {
this(null);
}
protected LeaderDeserializer(Class<?> vc) {
super(vc);
}
@Override
public Leader deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
String leaderName = p.getValueAsString();
if (leaderName == null) {
return null;
}
return getLeaderByName(leaderName);
}
private Leader getLeaderByName(String leaderName) {
// TODO 查询数据库或通过其他操作返回leader详细信息
if ("忽必烈".equals(leaderName)) {
Leader leader = new Leader();
leader.setLeaderName(leaderName);
leader.setAge(79);
leader.setNation("蒙古族");
return leader;
}
return null;
}
}
然后应用序列化类,在leader属性上添加序列化和反序列化注解。
注意,不能添加在Leader类上,否则会改变Leader类的默认序列化和反序列化方式,这里序列化的目标依旧是Country
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Country {
private String countryName;
@JsonSerialize(using = LeaderSerializer.class)
@JsonDeserialize(using = LeaderDeserializer.class)
private Leader leader;
}
测试
@Test
void test6() throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
Leader leader = new Leader("忽必烈", 79, "蒙古族");
Country country = new Country("元朝", leader);
System.out.println(objectMapper.writeValueAsString(leader));
System.out.println(objectMapper.writeValueAsString(country));
}
输出结果
{"leaderName":"忽必烈","age":79,"nation":"蒙古族"}
{"countryName":"元朝","leader":"忽必烈"}
从输出结果可以看到,Leader的序列化方式依旧是正常的,但是Country中的leader属性序列化后输出的只有leaderName属性,已经实现了我们需要的功能。
再来测试反序列化
@Test
void test7() throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
String json = "{\"countryName\":\"元朝\",\"leader\":\"忽必烈\"}";
System.out.println(objectMapper.readValue(json, Country.class));
}
输出结果
Country(countryName=元朝, leader=Leader(leaderName=忽必烈, age=79, nation=蒙古族))
可以看到,结果也是正常的。
2、使用@JsonValue和@JsonCreator注解
首先将上面Country中leader属性上加的序列化注解和反序列化注解去掉,还原为最初,然后修改Leader类如下
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Leader {
@JsonValue
private String leaderName;
private Integer age;
private String nation;
@JsonCreator(mode = JsonCreator.Mode.DELEGATING)
public static Leader getLeaderByName(String leaderName) {
// TODO 查询数据库或通过其他操作返回leader详细信息
if ("忽必烈".equals(leaderName)) {
Leader leader = new Leader();
leader.setLeaderName(leaderName);
leader.setAge(79);
leader.setNation("蒙古族");
return leader;
}
return null;
}
}
注意:@JsonCreator注解所标注的类必须要是static的
再来执行上面的测试类test6(),输出结果如下
"忽必烈"
{"countryName":"元朝","leader":"忽必烈"}
对比序列化器,可以看到,使用@JsonValue注解已经将Leader类的序列化方式改变了,进而影响了Country类。再来执行test7()测试反序列化,结果与之前是一致的。但是执行下面的反序列化测试的时候却报了错,从报错信息可以看到是JSON识别错误,暂时还未找到解决方法,待后续补充完善。
@Test
void test8() throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
System.out.println(objectMapper.readValue("忽必烈", Leader.class));
}
com.fasterxml.jackson.core.JsonParseException: Unrecognized token '忽必烈': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false') at [Source: (String)"忽必烈"; line: 1, column: 4]
所以更推荐使用自定义序列化器和反序列化器的方式来实现。
到此这篇关于Java的Jackson自定义序列化详解的文章就介绍到这了,更多相关Jackson自定义序列化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
