[TOC]
前言
最近写了一个Controller
, 接收了一个复杂模型
在这个过程中遇到了很多的问题,最近梳理出来了,在这里分享一下
首先看一下 Controller
代码
@RequestMapping(value = "/", method = RequestMethod.POST)
public String test(@RequestBody BaseWriteOperation data) {
}
看下 BaseWriteOperation
长什么样子
package com.mongodb.operation;
public abstract class BaseWriteOperation implements AsyncWriteOperation<WriteConcernResult>, WriteOperation<WriteConcernResult> {
private final WriteConcern writeConcern;
private final MongoNamespace namespace;
private final boolean ordered;
private Boolean bypassDocumentValidation;
}
这个对象是 MongoDB
驱动代码里的一个 抽象类
列出一个子类
public class InsertOperation extends BaseWriteOperation {
private final List<InsertRequest> insertRequests;
}
我的场景基本介绍完了,可以看到有挺多东西需要注意的,包括
- 得到
spring boot
的object mapper
- 向
spring boot
的object mapper
注册Module
(也就是注册自定义序列化、反序列化逻辑) - 实现一个自定义反序列化逻辑
- 实现复杂模型的嵌套反序列化
SpringBoot 相关
得到 object mapper
这个知道了就很简单,不知道的时候会无从下手
import com.fasterxml.jackson.databind.ObjectMapper;
@Autowired
private ObjectMapper bootJacksonObjectMapper;
注册自定义 Module
首先我介绍一下,我们自己实现一个 Jackson
的 json
工具类的时候,是怎么注册的
直接贴一下完整的工具类代码
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTimeZone;
import com.fanggeek.mmm.common.serialize.BaseWriteOperationDeserializer;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.joda.JodaModule;
/**
* <br>json 帮助类
* <br>对象和JSON互相转换
*
* @author YellowTail
* @since 2019-10-09
*/
public class JSON2Helper {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
static {
OBJECT_MAPPER.registerModule(new JodaModule());
OBJECT_MAPPER.registerModule(BaseWriteOperationDeserializer.getModule());
OBJECT_MAPPER.setTimeZone(DateTimeZone.getDefault().toTimeZone());
OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
OBJECT_MAPPER.setSerializationInclusion(Include.NON_NULL);
}
/**
* <br>得到实例
* @return
* @author YellowTail
* @since 2019-04-13
*/
public static ObjectMapper getInstance() {
return OBJECT_MAPPER;
}
/**
* 将Object对象转为JSON字符串
*
* @param object
* @return
*/
public static String toJson(Object object) {
String json = null;
try {
json = OBJECT_MAPPER.writeValueAsString(object);
} catch (Exception e) {
throw new RuntimeException("To json error, object is " + object + ";exception:" + e);
}
return json;
}
/**
* 将一个JSON字符串转换为Object对象
*
* @param <T>
* @param json
* @param clazz
* @return
*/
public static <T> T toObject(String json, Class<T> clazz) {
T o = null;
if (StringUtils.isNotBlank(json)) {
try {
o = OBJECT_MAPPER.readValue(json, clazz);
} catch (Exception e) {
throw new RuntimeException("Json string To object error, json is " + json + ";exception:" + e);
}
}
return o;
}
/**
* 将一个JSON字符串转换为T对象
*
* @param json
* @param typeReference
* @return
*/
public static <T> T toObject(String json, TypeReference<T> typeReference) {
try {
return OBJECT_MAPPER.readValue(json, typeReference);
} catch (Exception e) {
throw new RuntimeException("failed to convert string " + json + " to object", e);
}
}
}
注册逻辑在 static
方法块里的 registerModule
有一个自定义的方法,贴一下代码
BaseWriteOperationDeserializer.getModule();
public static SimpleModule getModule() {
SimpleModule simpleModule = new SimpleModule();
simpleModule.addDeserializer(BaseWriteOperation.class, new BaseWriteOperationDeserializer());
return simpleModule;
}
代码的意思后面会介绍,这里只介绍注册思路:就是向 Object Mapper 对象 register
Module
对于 spring boot
, 我们可以先拿到对象,再自己调用 registerModule
,这种是可以的,不过比较 low b
spring boot
提供了一个注解
import org.springframework.boot.jackson.JsonComponent;
@JsonComponent
我们写一个类,加上这个注解,继承抽象类 JsonDeserializer
或者 StdDeserializer
就可以了
下面介绍一下,如何去实现
Jackson 自定义序列化
反序列化
反序列化
就是把 一个 json
字符串 转换成 bean
或者 对象的一个方法/过程
首先看一下抽象方法,也就是我们要实现的方法
@JsonComponent
public class BaseWriteOperationDeserializer extends JsonDeserializer<BaseWriteOperation> {
public static SimpleModule getModule() {
SimpleModule simpleModule = new SimpleModule();
simpleModule.addDeserializer(BaseWriteOperation.class, new BaseWriteOperationDeserializer());
return simpleModule;
}
@SuppressWarnings("unused")
public BaseWriteOperation deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
//result, 也就是我们将要返回的对象
BaseWriteOperation result = null;
//得到 codec, 通过调试可以知道,ta是一个 object mapper 对象
ObjectCodec codec = p.getCodec();
//读出所有的字段,得到 TreeNode 对象
TreeNode readValueAsTree = p.readValueAsTree();
//得到某个属性 的 node
TreeNode treeNode = readValueAsTree.get("writeConcern");
//treeToValue 是一个 嵌套的反序列化操作,如果对象符合 jackson 反序列化规则(如有空构造方法, 有 get/set 方法等),那么调用之后,jackson自动进行反序列化
// 在这里,WriteConcern 是一个复杂对象,jackson 无法帮助我们自动反序列化,
//所以我们需要自己实现,也即是再写一个 public class WriteConcernDeserializer extends JsonDeserializer<WriteConcern> 即可
WriteConcern writeConcern = codec.treeToValue(treeNode, WriteConcern.class);
//这是一个复杂模型,也需要我们自己实现,此处不再赘述
TreeNode namespace = readValueAsTree.get("namespace");
MongoNamespace mongoNamespace = codec.treeToValue(namespace, MongoNamespace.class);
//这是一个 boolean 字段,可以直接得到对象
BooleanNode treeNode2 = (BooleanNode) readValueAsTree.get("ordered");
boolean ordered = treeNode2.asBoolean();
TreeNode treeNode3 = readValueAsTree.get("insertRequests");
...
代码省略
...
return result;
}
}
说一下一般流程和逻辑
- 首先得到一个
codec
对象,p.getCodec()
可以把该对象里面的复杂对象反序列化出来 - 得到
TreeNode 对象
,p.readValueAsTree()
- 通过
readValueAsTree.get("xxx")
得到不同属性的node
对象,再根据不同的类型,进行不同的操作,如果是简单类型,可以调用asXXX
得到值,如果是复杂对象,通过codec.treeToValue(xxNode, XXX.class)
得到复杂模型,如果这个嵌套的复杂模型不能被自动反序列化出来,那么就需要另外实现一下,实现完成之后,这里的treeToValue
调用之后,会自动进入嵌套的对象的反序列化代码里
序列化
序列化
就是把一个对象/bean 转成 json
字符串的一个 过程/方法
这里列两个例子
第一个,是普通对象 {"key": "value"}
@JsonComponent
public class WriteConcernSerializer extends JsonSerializer<WriteConcern> {
@Override
public void serialize(WriteConcern value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeStartObject();
Object wObject = value.getWObject();
if (null == wObject) {
gen.writeNullField("w");
}
gen.writeBooleanField("fsync", value.getFsync());
gen.writeBooleanField("journal", value.getJ());
gen.writeNumberField("wTimeoutMS", value.getWtimeout());
gen.writeEndObject();
}
}
gen.writeStartObject()
就是写入 {
gen.writeEndObject();
就是写入 }
中间的属性,根据不同的类型,调用不同的方法即可
第二个例子,我们有其他手段,或者方法可以得到 json
字符串的时候
public class BsonDocumentSerializer extends StdSerializer<BsonDocument> {
private static final long serialVersionUID = -3916419648325416357L;
protected BsonDocumentSerializer(Class<BsonDocument> t) {
super(t);
}
@Override
public void serialize(BsonDocument value, JsonGenerator gen, SerializerProvider provider) throws IOException {
String string = value.toJson();
gen.writeString(string);
}
}
如上所示,我们的 value
提供了一个 toJson
方法,可以直接得到 json
字符串,那么我们直接调用 writeString
写入就可以了
注意:此时不需要 writeStartObject
和 writeEndObject
了,因为这个字符串已经有这两个字符了
网友评论