文章目录

  • 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时,需要处理以下几种情况:

  • JsonTokennull,返回空集合
  • 字符串为空,返回空集合
  • 字符串不为空,用逗号分割为集合返回
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);// 注册模块