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> {
public static final String PAYLOAD = "payload";
public static final String MESSAGE_ID = "messageId";
public static final String TIMESTAMP = "timestamp";
public static final String POINT = "point";
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 {
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>> {
private static final long serialVersionUID = 7922419965896101563L;
public static final String MESSAGE_TYPE = "messageType";
public static final String PAYLOAD = "payload";
protected PayloadDeserialize() {
this(null);
}
protected PayloadDeserialize(Class<?> vc) {
super(vc);
}
@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 {
private static Map<String, DeserializeForType<Payload>> deserializeForTypeMap = new HashMap<>(4);
static {
add(new ModelDeserialize());
//如果有新的类型,直接在这里添加,这里也可以做成 spring容器管理进行注入
}
public static void add(DeserializeForType deserializeForType) {
Assertions.notNull(deserializeForType, "序列化器不能为空");
deserializeForTypeMap.putIfAbsent(deserializeForType.getType(), deserializeForType);
}
public static DeserializeForType<Payload> get(String type) {
Assertions.notBlank(type, "消息类型不能为空");
return deserializeForTypeMap.get(type);
}
}
interface DeserializeForType<T extends Payload> {
T deserialize(ObjectCodec codec, TreeNode node) throws IOException;
String getType();
}
public abstract static class AbstractDeserialize<T extends Payload> implements DeserializeForType<T> {
private final String type;
public AbstractDeserialize(String type) {
this.type = type;
}
@Override
public String getType() {
return this.type;
}
}
public static class ModelDeserialize extends AbstractDeserialize<Payload> {
public static final String TYPE = "MODEL";
public static final String MODEL_TYPE = "modelType";
private static final Map<String, DeserializeForType<Payload>> MODEL_TYPE_MAP = new HashMap<>(4);
static {
//这里根据 MODEL 类型又封装了几个子类型的方法进行转换
add(other());
add(event());
}
public ModelDeserialize() {
super(TYPE);
}
@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);
}
public static void add(DeserializeForType<Payload> deserializeForType) {
Assertions.notNull(deserializeForType, "对应类型的序列化器不能为空");
MODEL_TYPE_MAP.putIfAbsent(deserializeForType.getType(), deserializeForType);
}
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";
}
};
}
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反序列化器内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341