likes
comments
collection
share

Jackson 2.x 系列【26】Spring Boot 集成之 @JsonComponent、@JsonMixin、JsonObjectSerializer

作者站长头像
站长
· 阅读数 24

有道无术,术尚可求,有术无道,止于术。

本系列Jackson 版本 2.17.0

本系列Spring Boot 版本 3.2.4

源码地址:https://gitee.com/pearl-organization/study-jaskson-demo

1. @JsonComponent

在之前自定义序列化/反序列化器时,都需要我们自己调用API去注册它们,接下来学习Spring Boot中使用@JsonComponent注解进行注册,使用起来更加灵活方便。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface JsonComponent {

	/**
	 * 自定义 Bean 名称
	 */
	@AliasFor(annotation = Component.class)
	String value() default "";

	/**
	 * 序列化器/反序列化器所处理的类型
	 */
	Class<?>[] type() default {};

	/**
	 * 作用范围:VALUES-作用于值;KEYS-作用于Map 键
	 */
	Scope scope() default Scope.VALUES;

	// 省略其他......
}

案例演示 :将Jackson 2.x 系列【22】中自定义的序列化/反序列化器使用注解进行注册。

示例代码:

@JsonComponent
public class ListToStringComponent {

    public static class ListToStringJsonDeserializer extends StdDeserializer<List> {

        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);
        }
    }

    public static class ListToStringJsonSerializer extends StdSerializer<List> {

        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);
        }
    }
}

注入Spring Boot容器中提供的ObjectMapper

    @Autowired
    ObjectMapper objectMapper;

    @Test
    void testJsonComponent() throws JsonProcessingException {
        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=[管理员, 经理]}

2. @JsonMixin

Jackson提供了一种混合注解机制(mix-in annotations),允许开发者在不修改原始类的情况下,为其添加或覆盖特定的注解。

Spring Boot 2.7版本提供了@JsonMixin,启动时会扫描加载被@JsonMixin标识的类,不需要再显式的调用ObjectMapper#addMixIn方法声明关联关系,使用起来更加的方便。

@JsonMixin注解源码如下:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface JsonMixin {
    @AliasFor("type")
    Class<?>[] value() default {};

    @AliasFor("value")
    Class<?>[] type() default {};
}

Spring Boot工程中,混合类添加@JsonMixin注解,并指定原始类

@JsonMixin(TokenInfo.class) // 需要修改的目标类
public abstract class MixinTokenInfo {

    @JsonIgnore(value = false)
    String username;

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "GMT+8")
    Date birthday;
}

单元测试:

    @Autowired
    ObjectMapper objectMapper;


    @Test
    void testMixIn() throws JsonProcessingException {

        // 模拟第三包返回令牌对象
        TokenInfo tokenInfo=new TokenInfo();
        tokenInfo.setUsername("王法");
        tokenInfo.setBirthday(new Date());
        tokenInfo.setPassword("123456");

        // 序列化
        String value = objectMapper.writeValueAsString(tokenInfo);
        System.out.println(value);
    }

查看输出结果:

{"username":"王法","birthday":"2024-04-11"}

3. JsonObjectSerializer、JsonObjectDeserializer

Spring Boot提供了自定义序列化/反序列化器的基类,JsonObjectSerializer中实现了serialize序列化方法,其中调用了JsonGenerator 的``writeStartObject()writeEndObject()方法,也就是写出{ },由此可见它们的应用场景是处理Java Bean对象。

public abstract class JsonObjectSerializer<T> extends JsonSerializer<T> {

    public final void serialize(T value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
        try {
        	// {
            jgen.writeStartObject();
            this.serializeObject(value, jgen, provider);
            // }
            jgen.writeEndObject();
        } catch (Exception var6) {
            if (var6 instanceof IOException ioException) {
                throw ioException;
            } else {
                throw new JsonMappingException(jgen, "Object serialize error", var6);
            }
        }
    }
	// 待子类实现
    protected abstract void serializeObject(T value, JsonGenerator jgen, SerializerProvider provider) throws IOException;
}

JsonObjectSerializer中实现了deserialize反序列化方法,将JSON转换为树模型进行处理:

public abstract class JsonObjectDeserializer<T> extends JsonDeserializer<T> {

    public final T deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
        try {
            ObjectCodec codec = jp.getCodec();
            JsonNode tree = (JsonNode)codec.readTree(jp);
            return this.deserializeObject(jp, ctxt, codec, tree);
        } catch (Exception var5) {
            if (var5 instanceof IOException ioException) {
                throw ioException;
            } else {
                throw new JsonMappingException(jp, "Object deserialize error", var5);
            }
        }
    }

    protected abstract T deserializeObject(JsonParser jsonParser, DeserializationContext context, ObjectCodec codec, JsonNode tree) throws IOException;

    protected final <D> D nullSafeValue(JsonNode jsonNode, Class<D> type) {
        Assert.notNull(type, "Type must not be null");
        if (jsonNode == null) {
            return null;
        } else if (type == String.class) {
            return jsonNode.textValue();
        } else if (type == Boolean.class) {
            return jsonNode.booleanValue();
        } else if (type == Long.class) {
            return jsonNode.longValue();
        } else if (type == Integer.class) {
            return jsonNode.intValue();
        } else if (type == Short.class) {
            return jsonNode.shortValue();
        } else if (type == Double.class) {
            return jsonNode.doubleValue();
        } else if (type == Float.class) {
            return jsonNode.floatValue();
        } else if (type == BigDecimal.class) {
            return jsonNode.decimalValue();
        } else if (type == BigInteger.class) {
            return jsonNode.bigIntegerValue();
        } else {
            throw new IllegalArgumentException("Unsupported value type " + type.getName());
        }
    }

    protected final JsonNode getRequiredNode(JsonNode tree, String fieldName) {
        Assert.notNull(tree, "Tree must not be null");
        JsonNode node = tree.get(fieldName);
        Assert.state(node != null && !(node instanceof NullNode), () -> {
            return "Missing JSON field '" + fieldName + "'";
        });
        return node;
    }
}
转载自:https://juejin.cn/post/7377285753736970291
评论
请登录