本篇简单写写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,那就需要定制序列化方式。
首先定义一个序列化器,继承自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=蒙古族))
可以看到,结果也是正常的。
@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]
所以更推荐使用自定义序列化器和反序列化器的方式来实现。
上一篇:如何通过收集单词来组合单词
下一篇:核心词是什么