Java整合Jackson實現(xiàn)反序列化器流程
在使用 Jackson
時很多時候在自定義的數(shù)據(jù)結(jié)構(gòu)時通過本身自帶的序列化器可能實現(xiàn)不了自定義的結(jié)構(gòu)轉(zhuǎn)換,比如:需要從Json
字符串中讀取某個字段,根據(jù)某個字段轉(zhuǎn)換為對應(yīng)的實體類,例如下面的結(jié)構(gòu),我需要根據(jù) Json
字符串中的 messageType
字段轉(zhuǎn)換為對應(yīng)的實體,這時候我們可以通過自定義序列化器來進(jìn)行轉(zhuǎn)換
{ "messageId": "1665709790068", "timestamp": 1665709790068, "point": "123.33, 123.555", "messageType": "MODEL", "payload": { "method": "POST", "modelType": "EVENT", "output": { "ouaaa": "name" }, "timestamp": 1665709790068 } }
1. 實體類
這里 payload
可以使用成泛型通過指定對應(yīng)的類型來轉(zhuǎn)換為對應(yīng)的實體類型
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
實現(xiàn)類,這里我們定義一個就行了 ModelType
定義的枚舉類型,隨便寫就行了,只要跟后面獲取序列化器對應(yīng)就好
@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. 反序列化器
反序列化器的整體結(jié)構(gòu)
PayloadDeserialize 實現(xiàn)了 Jackson
的 StdDeserializer
序列化器
- 其中用到了
parser
里面的ObjectCodec
對象編碼,用于將對應(yīng)的Json
格式轉(zhuǎn)換為實體 - Jackon會將
Json
字符串中每個結(jié)構(gòu)都會轉(zhuǎn)換對應(yīng)的類型添加到樹結(jié)構(gòu)中,例如:字符串就會轉(zhuǎn)換為TextNode
,對象就會轉(zhuǎn)換為ObjectNode
其中根據(jù)每個轉(zhuǎn)換類型,我這里封裝了一個 DeserializeForType<Payload>
接口類型,用于根據(jù)自定的類型獲取序列化器,用于替換寫多個 If else
代碼不美觀
- 接口里面定義一個
type()
方法,實現(xiàn)類用于實現(xiàn),當(dāng)前類用于什么類型的轉(zhuǎn)換 - 上面
Json
接口中,通過messageType
轉(zhuǎn)換了之后可能還需要通過實體中的modelType
來進(jìn)行轉(zhuǎn)換,這里我又通過實現(xiàn)DeserializeForType<Payload>
創(chuàng)建了兩個匿名類event()
和other()
方法返回,先根據(jù)messageType
轉(zhuǎn)換了,然后再根據(jù)modelType
獲取對應(yīng)的反序列化器進(jìn)行轉(zhuǎn)換
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); } /** * 實現(xiàn)jackson序列化器的方法 */ @Override public UniversalMessage<Payload> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { //獲取到對象編碼對象,用當(dāng)前對應(yīng)可以對字符串進(jìn)行轉(zhuǎn)換 ObjectCodec codec = jsonParser.getCodec(); //讀取出整個消息體的樹型結(jié)構(gòu) TreeNode treeNode = codec.readTree(jsonParser); //解析出的節(jié)點樹結(jié)構(gòu),消息字段是處于第一層結(jié)構(gòu),所以這里可以直接通過get進(jìn)行獲取,jackson會將字符串不同的結(jié)構(gòu)解析為不同的 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(); //構(gòu)建外層實體消息 UniversalMessage<Payload> universalMessage = getPayloadUniversalMessage(treeNode, codec); //獲取到j(luò)son字符串中的 payload TreeNode payloadNode = treeNode.get(PAYLOAD); DeserializeForType<Payload> deserializeForType = DeserializeTypeContext.get(messageTypeStr); Objects.requireNonNull(deserializeForType, "不支持當(dāng)前消息類型:" + messageTypeStr); //對 payload進(jìn)行解析 Payload payload = deserializeForType.deserialize(codec, payloadNode); universalMessage.setPayload(payload); return universalMessage; } /** * 創(chuàng)建最外層的消息體 */ 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) //枚舉轉(zhuǎn)換的工具,這里可以自己封裝一個,根據(jù)名稱進(jìn)行轉(zhuǎn)換 .messageType(EnumUtils.nameOf(MessageType.class, messageType.toUpperCase())) .timestamp(timestamp) .point(point).build(); } /** * 保存序列化器的上下文,為防止反復(fù)的創(chuàng)建序列化器,這里我們提前加載上對類型的序列化器 */ static class DeserializeTypeContext { /** Deserialize for type map */ private static Map<String, DeserializeForType<Payload>> deserializeForTypeMap = new HashMap<>(4); static { add(new ModelDeserialize()); //如果有新的類型,直接在這里添加,這里也可以做成 spring容器管理進(jìn)行注入 } /** * 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對象是上面最外層的方法傳遞進(jìn)來的,TreeNode則是對應(yīng)的消息結(jié)構(gòu)體 */ T deserialize(ObjectCodec codec, TreeNode node) throws IOException; /** * Gets type */ String getType(); } /** * 封裝一個抽象的父級類,主要用于實現(xiàn)接口的 Type 方法,獲取當(dāng)前類是什么類型的 */ 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 { //這里根據(jù) MODEL 類型又封裝了幾個子類型的方法進(jìn)行轉(zhuǎn)換 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 類型的字段會被轉(zhuǎn)換成 TextNode的節(jié)點 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, "對應(yīng)類型的序列化器不能為空"); 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. 序列化器
序列化器就比較簡單了,可以先根據(jù) payload
實體獲取到對應(yīng)的類型,寫入一個 string類型 messageType
然后再寫 payload
作為對象進(jìn)行寫成 Json
格式(其他幾個字段我就不寫了,根據(jù)對應(yīng)的類型調(diào)用對應(yīng)類型的 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(); }
到此這篇關(guān)于Java整合Jackson實現(xiàn)反序列化器流程的文章就介紹到這了,更多相關(guān)Java Jackson反序列化器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
idea中創(chuàng)建jsp項目的詳細(xì)實戰(zhàn)步驟
才學(xué)javaWeb,以防自己忘記創(chuàng)建項目的過程,所以淺淺的記錄一下吧,下面這篇文章主要給大家介紹了關(guān)于idea中創(chuàng)建jsp項目的詳細(xì)步驟,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09一文學(xué)會使用sa-token解決網(wǎng)站權(quán)限驗證
這篇文章主要為大家介紹了使用sa-token解決網(wǎng)站權(quán)限驗證方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07Maven學(xué)習(xí)----Maven安裝與環(huán)境變量配置教程
這篇文章主要給大家介紹了關(guān)于如何利用Maven入手Spring Boot第一個程序的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-06-06