Jackson自定义序列化
admin
2024-02-02 18:45:04

本篇简单写写Jackson中自定义序列化的方式。

假设有类CountryLeader,分别表示国家以及国家领导人,领导人除了姓名外还有年龄和民族,定义如下

@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 {public LeaderSerializer() {this(null);}protected LeaderSerializer(Class t) {super(t);}@Overridepublic 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 {public LeaderDeserializer() {this(null);}protected LeaderDeserializer(Class vc) {super(vc);}@Overridepublic 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 {@JsonValueprivate 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]

所以更推荐使用自定义序列化器和反序列化器的方式来实现。

相关内容

热门资讯

原创 从... 朱孝天道歉了。 就在他效仿“新晋导师”汪小菲,对着F3和旧日东家一番阴阳怪气、成功将自己送上热搜后...
士官选取和士兵退役问答 士官退...  做好士官选取和士兵退役工作,关系到士兵队伍建设质量和部队战斗力的巩固与提高,也直接涉及广大士兵的切...
军队改革解放军如何编制 七十年... 习近平在纪念中国人民抗日战争暨世界反法西斯战争胜利70周年大会发表重要讲话时表示,将裁军30万人。国...
最新或2023(历届)军队退役...  随着时间的推移,现在越来越多的地方都在争议如何涨工资以及退休之后有哪些养老金工资等,现在我们先来看...
关于军队退役的方案最新 军队退... 改革开放以来,我国社会主义经济建设取得了举世瞩目的成就,国防建设也得到了快速发展。然而,随着社会主义...