Java整合Jackson实现反序列化器流程

在使用 Jackson 时很多时候在自定义的数据结构时通过本身自带的序列化器可能实现不了自定义的结构转换,比如:需要从Json 字符串中读取某个字段,根据某个字段转换为对应的实体类,例如下面的结构,我需要根据 Json 字符串中的 messageType 字段转换为对应的实体,这时候我们可以通过自定义序列化器来进行转换

{
    "messageId": "1665709790068",
    "timestamp": 1665709790068,
    "point": "123.33, 123.555",
    "messageType": "MODEL",
    "payload": {
        "method": "POST",
        "modelType": "EVENT",
        "output": {
            "ouaaa": "name"
        },
        "timestamp": 1665709790068
    }
}

1. 实体类

这里 payload 可以使用成泛型通过指定对应的类型来转换为对应的实体类型

public class UniversalMessage<T extends Payload> {
    /** PAYLOAD */
    public static final String PAYLOAD = "payload";
    /** MESSAGE_ID */
    public static final String MESSAGE_ID = "messageId";
    /** TIMESTAMP */
    public static final String TIMESTAMP = "timestamp";
    /** POINT */
    public static final String POINT = "point";
    /** MESSAGETYPE */
    public static final String MESSAGE_TYPE = "messageType";
    private static final long serialVersionUID = -3703724430631400996L;
    protected String messageId;
    protected Long timestamp;
    protected String point;
    protected MessageType messageType;
    private T payload;
}

下面是定义好的 Payload 实现类,这里我们定义一个就行了 ModelType 定义的枚举类型,随便写就行了,只要跟后面获取序列化器对应就好

@Type(value = "MODEL") //填写消息类型
public class ModelEventPayload implements Payload {
    /** serialVersionUID */
    private static final long serialVersionUID = -4371712921890795815L;
    private Map<String, Object> output;
    private Long timestamp;
    protected ModelType modelType;
    protected String method;
}

2. 反序列化器

反序列化器的整体结构

PayloadDeserialize 实现了 JacksonStdDeserializer 序列化器

  • 其中用到了 parser 里面的 ObjectCodec 对象编码,用于将对应的 Json 格式转换为实体
  • Jackon会将 Json 字符串中每个结构都会转换对应的类型添加到树结构中,例如:字符串就会转换为 TextNode,对象就会转换为 ObjectNode

其中根据每个转换类型,我这里封装了一个 DeserializeForType<Payload> 接口类型,用于根据自定的类型获取序列化器,用于替换写多个 If else 代码不美观

  • 接口里面定义一个 type() 方法,实现类用于实现,当前类用于什么类型的转换
  • 上面 Json 接口中,通过 messageType 转换了之后可能还需要通过实体中的 modelType 来进行转换,这里我又通过实现 DeserializeForType<Payload> 创建了两个匿名类 event()other() 方法返回,先根据 messageType 转换了,然后再根据 modelType 获取对应的反序列化器进行转换
public class PayloadDeserialize extends StdDeserializer<UniversalMessage<Payload>> {
    /** serialVersionUID */
    private static final long serialVersionUID = 7922419965896101563L;
    /** MESSAGE_TYPE */
    public static final String MESSAGE_TYPE = "messageType";
    /** PAYLOAD */
    public static final String PAYLOAD = "payload";
    /**
     * Payload deserialize
     */
    protected PayloadDeserialize() {
        this(null);
    }
    /**
     * Payload deserialize
     */
    protected PayloadDeserialize(Class<?> vc) {
        super(vc);
    }
    /**
     * 实现jackson序列化器的方法
     */
    @Override
    public UniversalMessage<Payload> deserialize(JsonParser jsonParser,
    DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
        //获取到对象编码对象,用当前对应可以对字符串进行转换
        ObjectCodec codec = jsonParser.getCodec();
        //读取出整个消息体的树型结构
        TreeNode treeNode = codec.readTree(jsonParser);
        //解析出的节点树结构,消息字段是处于第一层结构,所以这里可以直接通过get进行获取,jackson会将字符串不同的结构解析为不同的 TreeNode类型,例如字符串就会解析成 TextNode
        TreeNode messageTypeNode = Objects.requireNonNull(treeNode.get(MESSAGE_TYPE), "消息类型不能为空");
        Class<? extends TreeNode> messageTypeNodeClass = messageTypeNode.getClass();
        boolean assignableFrom = TextNode.class.isAssignableFrom(messageTypeNodeClass);
        Assertions.isTrue(assignableFrom, "消息类型字段类型错误:" + messageTypeNodeClass + "需要 TextNode");
        String messageTypeStr = ((TextNode) messageTypeNode).asText();
        //构建外层实体消息
        UniversalMessage<Payload> universalMessage = getPayloadUniversalMessage(treeNode, codec);
        //获取到json字符串中的 payload
        TreeNode payloadNode = treeNode.get(PAYLOAD);
        DeserializeForType<Payload> deserializeForType = DeserializeTypeContext.get(messageTypeStr);
        Objects.requireNonNull(deserializeForType, "不支持当前消息类型:" + messageTypeStr);
        //对 payload进行解析
        Payload payload = deserializeForType.deserialize(codec, payloadNode);
        universalMessage.setPayload(payload);
        return universalMessage;
    }
    /**
     * 创建最外层的消息体
     */
    private UniversalMessage<Payload> getPayloadUniversalMessage(TreeNode treeNode, ObjectCodec codec) throws IOException {
        ObjectNode node = (ObjectNode) treeNode;
        String messageId = Objects.requireNonNull(node.get(UniversalMessage.MESSAGE_ID), "消息ID不能为空").asText();
        String messageType = Objects.requireNonNull(node.get(UniversalMessage.MESSAGE_TYPE), "消息类型不能为空").asText();
        long timestamp = Objects.requireNonNull(node.get(UniversalMessage.TIMESTAMP), "时间戳不能为空").asLong();
        String point = Objects.requireNonNull(node.get(UniversalMessage.POINT), "Point不能为空").asText();
        return UniversalMessage.builder()
            .messageId(messageId)
            //枚举转换的工具,这里可以自己封装一个,根据名称进行转换
            .messageType(EnumUtils.nameOf(MessageType.class, messageType.toUpperCase()))
            .timestamp(timestamp)
            .point(point).build();
    }
    /**
     * 保存序列化器的上下文,为防止反复的创建序列化器,这里我们提前加载上对类型的序列化器
     */
    static class DeserializeTypeContext {
        /** Deserialize for type map */
        private static Map<String, DeserializeForType<Payload>> deserializeForTypeMap = new HashMap<>(4);
        static {
            add(new ModelDeserialize());
            //如果有新的类型,直接在这里添加,这里也可以做成 spring容器管理进行注入
        }
        /**
         * Add
         */
        public static void add(DeserializeForType deserializeForType) {
            Assertions.notNull(deserializeForType, "序列化器不能为空");
            deserializeForTypeMap.putIfAbsent(deserializeForType.getType(), deserializeForType);
        }
        /**
         * Get
         */
        public static DeserializeForType<Payload> get(String type) {
            Assertions.notBlank(type, "消息类型不能为空");
            return deserializeForTypeMap.get(type);
        }
    }
    /**
     * 定义的序列化器顶级接口
     */
    interface DeserializeForType<T extends Payload> {
        /**
         * 反序列化方法,这里的 ObjectCodec对象是上面最外层的方法传递进来的,TreeNode则是对应的消息结构体
         */
        T deserialize(ObjectCodec codec,  TreeNode node) throws IOException;
        /**
         * Gets type
         */
        String getType();
    }
    /**
     * 封装一个抽象的父级类,主要用于实现接口的 Type 方法,获取当前类是什么类型的
     */
    public abstract static class AbstractDeserialize<T extends Payload> implements DeserializeForType<T> {
        /** Type */
        private final String type;
        /**
         * Abstract deserialize
         */
        public AbstractDeserialize(String type) {
            this.type = type;
        }
        /**
         * Gets type *
         */
        @Override
        public String getType() {
            return this.type;
        }
    }
    /**
     * 自定义model类型的序列化器
     */
    public static class ModelDeserialize extends AbstractDeserialize<Payload> {
        /** TYPE */
        public static final String TYPE = "MODEL";
        /** MODEL_TYPE */
        public static final String MODEL_TYPE = "modelType";
        /** MODEL_TYPE_MAP */
        private static final Map<String, DeserializeForType<Payload>> MODEL_TYPE_MAP = new HashMap<>(4);
        static {
                //这里根据 MODEL 类型又封装了几个子类型的方法进行转换
            add(other());
            add(event());
        }
        /**
         * Model deserialize
         */
        public ModelDeserialize() {
            super(TYPE);
        }
        /**
         * Deserialize
         */
        @Override
        public Payload deserialize(ObjectCodec codec, TreeNode node) throws IOException {
            TreeNode modelTypeNode = Objects.requireNonNull(node.get(MODEL_TYPE), "消息类型不能为空");
            Class<? extends TreeNode> modelTypeNodeClass = modelTypeNode.getClass();
            boolean assignableFrom = TextNode.class.isAssignableFrom(modelTypeNodeClass);
            Assertions.isTrue(assignableFrom, "模型消息类型字段类型错误:" + modelTypeNodeClass + "需要 TextNode");
            //string 类型的字段会被转换成 TextNode的节点
            String messageTypeStr = ((TextNode) modelTypeNode).asText();
            DeserializeForType<Payload> deserializeForType = MODEL_TYPE_MAP.get(messageTypeStr);
            Assertions.notNull(deserializeForType, "模型:" + messageTypeStr + ",序列化器为空");
            return deserializeForType.deserialize(codec, node);
        }
        /**
         * Add
         */
        public static void add(DeserializeForType<Payload> deserializeForType) {
            Assertions.notNull(deserializeForType, "对应类型的序列化器不能为空");
            MODEL_TYPE_MAP.putIfAbsent(deserializeForType.getType(), deserializeForType);
        }
        /**
         * OTHER
         */
        private static DeserializeForType<Payload> other() {
            return new DeserializeForType<Payload>() {
                @Override
                public Payload deserialize(ObjectCodec codec, TreeNode node) throws IOException {
                    return node.traverse(codec).readValueAs(ModelOtherPayload.class);
                }
                @Override
                public String getType() {
                    return "OTHER";
                }
            };
        }
        /**
         * Event
         */
        private static DeserializeForType<Payload> event() {
            return new DeserializeForType<Payload>() {
                @Override
                public Payload deserialize(ObjectCodec codec, TreeNode node) throws IOException {
                    return node.traverse(codec).readValueAs(ModelEventPayload.class);
                }
                @Override
                public String getType() {
                    return "EVENT";
                }
            };
        }
    }
}

3. 序列化器

序列化器就比较简单了,可以先根据 payload 实体获取到对应的类型,写入一个 string类型 messageType 然后再写 payload 作为对象进行写成 Json 格式(其他几个字段我就不写了,根据对应的类型调用对应类型的 write方法即可)

public class PayloadSerialize extends StdSerializer<UniversalMessage<Payload>> {
    private static final long serialVersionUID = 7679701332948432903L;
    protected PayloadSerialize() {
        this(null);
    }
    protected PayloadSerialize(Class<Payload> t) {
        super(t);
    }
    @Override
    public void serialize(UniversalMessage<Payload> value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        Payload payload = value.getPayload();
        gen.writeStartObject();
        if (payload != null) {
            Type type = payload.getClass().getAnnotation(Type.class);
            if (type != null) {
                gen.writeStringField("messageType", type.value());
            }
        }
        gen.writeObjectField("payload", payload);
        gen.writeEndObject();
    }
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Type {
    String value();
}

原文地址:https://blog.csdn.net/weixin_43915643/article/details/127313547