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 实现了 Jackson
的 StdDeserializer
序列化器
- 其中用到了
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(); }
到此这篇关于Java整合Jackson实现反序列化器流程的文章就介绍到这了,更多相关Java Jackson反序列化器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
最新评论