文章目录
- 1. 前言
- 2. 案例演示
- 2.1 序列化器
- 2.2 反序列化器
- 3. 使用方式
- 3.1 注解
- 3.2 模块
- 3.3 修饰器
1. 前言
在实际开发中,Jackson
核心模块支持了常见类型的数据转换,此外还有很多第三方模块支持不常用的类型,基本已经够用。但是在某些特殊场景下,可能需要支持自定义类型或者转换规则。
2. 案例演示
演示需求:
- 后端查询的角色列表是一个字符串集合,需要反序列化为逗号分割的字符串,用于前端直接展示
- 前端提交的是逗号分割的字符串,需要反序列化为集合
Java Bean
如下:
public class UserVO {
Long id;
String username;
List<String> roleList;
// 省略.............
}
使用hutool
工具类:
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.26</version>
</dependency>
2.1 序列化器
自定义序列化器,之前有说过需要继承StdSerializer
,而不是JsonSerializer
,指定泛型为被序列化的数据类型List
,实现序列化方法,集合为空时写入空字符串“”
,不为空时转为逗号拼接的字符串:
public class ListToStringJsonSerializer extends StdSerializer<List> {
public static final ListToStringJsonSerializer instance = new ListToStringJsonSerializer();
public ListToStringJsonSerializer() {
super(String.class, false);
}
/**
* 序列化
*
* @param value 被序列化的值
* @param jsonGenerator 生成器
* @param serializerProvider 提供者
* @throws IOException
*/
@Override
public void serialize(List value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
// 空集合值写入 “”
if (CollUtil.isEmpty(value)) {
jsonGenerator.writeString("");
} else {
// 写入转为“,”拼接的字符串
String commaString = CollUtil.join(value, StrUtil.COMMA);
jsonGenerator.writeString(commaString);
}
}
public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException {
this.visitStringFormat(visitor, typeHint);
}
}
2.2 反序列化器
自定义反序列化器继承StdDeserializer
,指定泛型反序列化后的数据类型List
,实现反序列化方法。
当解析读取到的JsonToken
时,需要处理以下几种情况:
-
JsonToken
为null
,返回空集合 - 字符串为空,返回空集合
- 字符串不为空,用逗号分割为集合返回
public class ListToStringJsonDeserializer extends StdDeserializer<List> {
public static final ListToStringJsonDeserializer instance = new ListToStringJsonDeserializer();
public ListToStringJsonDeserializer() {
super(String.class);
}
/**
* 反序列化
*
* @param jsonParser 解析器
* @param deserializationContext 上下文
* @return 反序列化后的值
* @throws IOException
*/
@Override
public List<String> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException {
if (jsonParser.hasToken(JsonToken.VALUE_NULL)) { // null 返回空集合
return new ArrayList<>();
} else if (jsonParser.hasToken(JsonToken.VALUE_STRING)) { // 当前是String类型
String textValue = jsonParser.getText();// 原值
// 空值返回空集合
if (textValue.isEmpty() || _isBlank(textValue)) {
return new ArrayList<>();
}
// “,”分割,返回集合
return StrUtil.split(textValue, StrUtil.COMMA, true, true);
}
throw JsonMappingException.from(jsonParser, "JSON有误");
}
public LogicalType logicalType() {
return LogicalType.Textual;
}
public boolean isCachable() {
return true;
}
public Object getEmptyValue(DeserializationContext ctxt) throws JsonMappingException {
return "";
}
public List<?> deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException {
return this.deserialize(p, ctxt);
}
}
3. 使用方式
定义好了序列化/反序列化器后,Jackson
支持多种方式添加它们到执行过程中。
3.1 注解
使用@JsonSerialize
、@JsonDeserialize
指定序列化/反序列化器,这种方式粒度最细,优先级最高。
@JsonSerialize(using = ListToStringJsonSerializer.class)
@JsonDeserialize(using = ListToStringJsonDeserializer.class)
List<String> roleList;
测试代码:
ObjectMapper objectMapper = new ObjectMapper();
UserVO userVO = new UserVO();
userVO.setId(1699657986705854464L);
userVO.setUsername("jack");
List<String> roleList=new ArrayList<>();
roleList.add("管理员");
roleList.add("经理");
userVO.setRoleList(roleList);
// 序列化
String userVoJson = objectMapper.writeValueAsString(userVO);
System.out.println(userVoJson);
// 反序列化
String json="{\"id\":1699657986705854464,\"username\":\"jack\",\"roleList\":\"管理员,经理\"}";
UserVO readValue = objectMapper.readValue(json, UserVO.class);
System.out.println(readValue);
输出结果:
{"id":1699657986705854464,"username":"jack","roleList":"管理员,经理"}
UserVO{id=1699657986705854464, username='jack', roleList=[管理员, 经理]}
还可以使用组合注解的方式,更加方便:
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = ListToStringJsonSerializer.class)
@JsonDeserialize(using = ListToStringJsonDeserializer.class)
public @interface JsonListToCommaSplitString {
}
@JsonListToCommaSplitString
List<String> roleList;
3.2 模块
在自定义模块中,也可以注册自定义序列化/反序列化器,但是这样作用的是全局对象,这里不太合适:
SimpleModule customModule = new SimpleModule(); // 自定义模块
// 注册自定义的序列化器/反序列化器
customModule.addSerializer(List.class,ListToStringJsonSerializer.instance);
customModule.addDeserializer(List.class, ListToStringJsonDeserializer.instance);
objectMapper.registerModule(customModule); // 注册自定义模块
3.3 修饰器
使用Java Bean
序列化/反序列化修饰器提供的钩子方法,替换集合类型的序列化/反序列化器,作用于全局的Java Bean
,这里不太合适。
public class ListToStringBeanSerializerModifier extends BeanSerializerModifier {
public JsonSerializer<?> modifyCollectionSerializer(SerializationConfig config, CollectionType valueType, BeanDescription beanDesc, JsonSerializer<?> serializer) {
return ListToStringJsonSerializer.instance;
}
}
public class ListToStringBeanBeanDeserializerModifier extends BeanDeserializerModifier {
public JsonDeserializer<?> modifyCollectionDeserializer(DeserializationConfig config, CollectionType type, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
return ListToStringJsonDeserializer.instance;
}
}
使用模块注册:
// 使用模块注册BeanModifier:
SimpleModule simpleModule=new SimpleModule("myModule");
simpleModule.setSerializerModifier(new ListToStringBeanSerializerModifier());
simpleModule.setDeserializerModifier(new ListToStringBeanBeanDeserializerModifier());
objectMapper.registerModule(simpleModule);// 注册模块