Java整合Jackson實(shí)現(xiàn)反序列化器流程
在使用 Jackson 時(shí)很多時(shí)候在自定義的數(shù)據(jù)結(jié)構(gòu)時(shí)通過(guò)本身自帶的序列化器可能實(shí)現(xiàn)不了自定義的結(jié)構(gòu)轉(zhuǎn)換,比如:需要從Json 字符串中讀取某個(gè)字段,根據(jù)某個(gè)字段轉(zhuǎn)換為對(duì)應(yīng)的實(shí)體類,例如下面的結(jié)構(gòu),我需要根據(jù) Json 字符串中的 messageType 字段轉(zhuǎn)換為對(duì)應(yīng)的實(shí)體,這時(shí)候我們可以通過(guò)自定義序列化器來(lái)進(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. 實(shí)體類
這里 payload 可以使用成泛型通過(guò)指定對(duì)應(yīng)的類型來(lái)轉(zhuǎn)換為對(duì)應(yīng)的實(shí)體類型
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 實(shí)現(xiàn)類,這里我們定義一個(gè)就行了 ModelType 定義的枚舉類型,隨便寫就行了,只要跟后面獲取序列化器對(duì)應(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 實(shí)現(xiàn)了 Jackson 的 StdDeserializer 序列化器
- 其中用到了
parser里面的ObjectCodec對(duì)象編碼,用于將對(duì)應(yīng)的Json格式轉(zhuǎn)換為實(shí)體 - Jackon會(huì)將
Json字符串中每個(gè)結(jié)構(gòu)都會(huì)轉(zhuǎn)換對(duì)應(yīng)的類型添加到樹結(jié)構(gòu)中,例如:字符串就會(huì)轉(zhuǎn)換為TextNode,對(duì)象就會(huì)轉(zhuǎn)換為ObjectNode
其中根據(jù)每個(gè)轉(zhuǎn)換類型,我這里封裝了一個(gè) DeserializeForType<Payload> 接口類型,用于根據(jù)自定的類型獲取序列化器,用于替換寫多個(gè) If else 代碼不美觀
- 接口里面定義一個(gè)
type()方法,實(shí)現(xiàn)類用于實(shí)現(xiàn),當(dāng)前類用于什么類型的轉(zhuǎn)換 - 上面
Json接口中,通過(guò)messageType轉(zhuǎn)換了之后可能還需要通過(guò)實(shí)體中的modelType來(lái)進(jìn)行轉(zhuǎn)換,這里我又通過(guò)實(shí)現(xiàn)DeserializeForType<Payload>創(chuàng)建了兩個(gè)匿名類event()和other()方法返回,先根據(jù)messageType轉(zhuǎn)換了,然后再根據(jù)modelType獲取對(duì)應(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);
}
/**
* 實(shí)現(xiàn)jackson序列化器的方法
*/
@Override
public UniversalMessage<Payload> deserialize(JsonParser jsonParser,
DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
//獲取到對(duì)象編碼對(duì)象,用當(dāng)前對(duì)應(yīng)可以對(duì)字符串進(jìn)行轉(zhuǎn)換
ObjectCodec codec = jsonParser.getCodec();
//讀取出整個(gè)消息體的樹型結(jié)構(gòu)
TreeNode treeNode = codec.readTree(jsonParser);
//解析出的節(jié)點(diǎn)樹結(jié)構(gòu),消息字段是處于第一層結(jié)構(gòu),所以這里可以直接通過(guò)get進(jìn)行獲取,jackson會(huì)將字符串不同的結(jié)構(gòu)解析為不同的 TreeNode類型,例如字符串就會(huì)解析成 TextNode
TreeNode messageTypeNode = Objects.requireNonNull(treeNode.get(MESSAGE_TYPE), "消息類型不能為空");
Class<? extends TreeNode> messageTypeNodeClass = messageTypeNode.getClass();
boolean assignableFrom = TextNode.class.isAssignableFrom(messageTypeNodeClass);
Assertions.isTrue(assignableFrom, "消息類型字段類型錯(cuò)誤:" + messageTypeNodeClass + "需要 TextNode");
String messageTypeStr = ((TextNode) messageTypeNode).asText();
//構(gòu)建外層實(shí)體消息
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);
//對(duì) 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), "時(shí)間戳不能為空").asLong();
String point = Objects.requireNonNull(node.get(UniversalMessage.POINT), "Point不能為空").asText();
return UniversalMessage.builder()
.messageId(messageId)
//枚舉轉(zhuǎn)換的工具,這里可以自己封裝一個(gè),根據(jù)名稱進(jìn)行轉(zhuǎn)換
.messageType(EnumUtils.nameOf(MessageType.class, messageType.toUpperCase()))
.timestamp(timestamp)
.point(point).build();
}
/**
* 保存序列化器的上下文,為防止反復(fù)的創(chuàng)建序列化器,這里我們提前加載上對(duì)類型的序列化器
*/
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);
}
}
/**
* 定義的序列化器頂級(jí)接口
*/
interface DeserializeForType<T extends Payload> {
/**
* 反序列化方法,這里的 ObjectCodec對(duì)象是上面最外層的方法傳遞進(jìn)來(lái)的,TreeNode則是對(duì)應(yīng)的消息結(jié)構(gòu)體
*/
T deserialize(ObjectCodec codec, TreeNode node) throws IOException;
/**
* Gets type
*/
String getType();
}
/**
* 封裝一個(gè)抽象的父級(jí)類,主要用于實(shí)現(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 類型又封裝了幾個(gè)子類型的方法進(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, "模型消息類型字段類型錯(cuò)誤:" + modelTypeNodeClass + "需要 TextNode");
//string 類型的字段會(huì)被轉(zhuǎn)換成 TextNode的節(jié)點(diǎn)
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, "對(duì)應(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. 序列化器
序列化器就比較簡(jiǎn)單了,可以先根據(jù) payload 實(shí)體獲取到對(duì)應(yīng)的類型,寫入一個(gè) string類型 messageType 然后再寫 payload 作為對(duì)象進(jìn)行寫成 Json 格式(其他幾個(gè)字段我就不寫了,根據(jù)對(duì)應(yīng)的類型調(diào)用對(duì)應(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實(shí)現(xiàn)反序列化器流程的文章就介紹到這了,更多相關(guān)Java Jackson反序列化器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
idea中創(chuàng)建jsp項(xiàng)目的詳細(xì)實(shí)戰(zhàn)步驟
才學(xué)javaWeb,以防自己忘記創(chuàng)建項(xiàng)目的過(guò)程,所以淺淺的記錄一下吧,下面這篇文章主要給大家介紹了關(guān)于idea中創(chuàng)建jsp項(xiàng)目的詳細(xì)步驟,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09
一文學(xué)會(huì)使用sa-token解決網(wǎng)站權(quán)限驗(yàn)證
這篇文章主要為大家介紹了使用sa-token解決網(wǎng)站權(quán)限驗(yàn)證方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07
JAVA 根據(jù)Url把多文件打包成ZIP下載實(shí)例
這篇文章主要介紹了JAVA 根據(jù)Url把多文件打包成ZIP下載的相關(guān)資料,需要的朋友可以參考下2017-08-08
java實(shí)現(xiàn)驗(yàn)證碼類生成中文驗(yàn)證碼
java實(shí)現(xiàn)的漢字輸入驗(yàn)證碼,主要包含兩個(gè)類,一個(gè)是生成驗(yàn)證碼,一個(gè)是判斷驗(yàn)證碼輸入是否正確,實(shí)現(xiàn)原理非常簡(jiǎn)單,將漢字和干擾線生成圖片并將漢字保存到session,前臺(tái)獲取每次生成驗(yàn)證碼圖片并用文本框值和session值比較,功能就怎么簡(jiǎn)單2014-01-01
使用maven工具解決jar包沖突或重復(fù)加載的問(wèn)題
這篇文章主要介紹了使用maven工具解決jar包沖突或重復(fù)加載的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09
Maven學(xué)習(xí)----Maven安裝與環(huán)境變量配置教程
這篇文章主要給大家介紹了關(guān)于如何利用Maven入手Spring Boot第一個(gè)程序的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-06-06

