java:jackson 二:Custom Deserialization in Jackson
1 前言
jackson支持自定义反序列化器,参考文档地址如下:
https://www.baeldung.com/jackson
https://www.baeldung.com/jackson-deserialization
依赖如下(这里使用jackson-databind的2.14.1版本):
<properties>
<jackson.version>2.14.1</jackson.version>
</properties>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
2 使用
2.1 Standard Deserialization
Let’s start by defining two entities and see how Jackson will deserialize a JSON representation to these entities without any customization:
package com.xiaoxu.test.jackson;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import lombok.Data;
import lombok.ToString;
/**
* @author xiaoxu
* @date 2022-12-22
* spring_boot:com.xiaoxu.test.jackson.TestCustomizeJackson
*/
public class TestCustomizeJackson {
private static ObjectMapper objectMapper = new ObjectMapper();
static {
objectMapper.configure(SerializationFeature.INDENT_OUTPUT,true);
}
public static void testStandard() throws Exception{
Good g = new Good();
g.setId(1);
g.setGoodName("苹果");
VIPUser vipUser = new VIPUser();
vipUser.setUid(999L);
vipUser.setUserName("xiaoxu");
g.setVipUser(vipUser);
String s = objectMapper.writeValueAsString(g);
System.out.println(s);
String msg = "{\n" +
" \"id\" : 1,\n" +
" \"goodName\" : \"苹果\",\n" +
" \"vipUser\" : {\n" +
" \"uid\" : 999,\n" +
" \"userName\" : \"xiaoxu\"\n" +
" }\n" +
"}";
Good good = objectMapper.readValue(msg, Good.class);
System.out.println(good);
}
public static void main(String[] args) throws Exception{
testStandard();
}
}
@Data
@ToString
class Good{
public int id;
public String goodName;
public VIPUser vipUser;
}
@Data
@ToString
class VIPUser{
long uid;
String userName;
}
执行如下:
{
"id" : 1,
"goodName" : "苹果",
"vipUser" : {
"uid" : 999,
"userName" : "xiaoxu"
}
}
Good(id=1, goodName=苹果, vipUser=VIPUser(uid=999, userName=xiaoxu))
上述场景不需要自定义反序列化器
2.2 Custom Deserializer on ObjectMapper
this will of course fail
package com.xiaoxu.test.jackson;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
/**
* @author xiaoxu
* @date 2022-12-22
* spring_boot:com.xiaoxu.test.jackson.TestCustomizeJackson2
*/
public class TestCustomizeJackson2 {
private static ObjectMapper objectMapper = new ObjectMapper();
static {
objectMapper.configure(SerializationFeature.INDENT_OUTPUT,true);
}
public static void testStandard2() throws Exception{
String msg = "{\n" +
" \"id\" : 1,\n" +
" \"goodName\" : \"苹果\",\n" +
" \"vipUser\" : {\n" +
" }\n" +
"}";
Good good = objectMapper.readValue(msg, Good.class);
System.out.println(good);
/* 将反序列化的字符串的key vipUser 改为 normalUser,
* 反序列化将报错*/
String msg2 = "{\n" +
" \"id\" : 1,\n" +
" \"goodName\" : \"苹果\",\n" +
" \"normalUser\" : {\n" +
" \"userName\" : \"tellMe\"\n" +
" }\n" +
"}";
Good good2 = objectMapper.readValue(msg2, Good.class);
System.out.println(good2);
}
public static void main(String[] args) throws Exception{
testStandard2();
}
}
执行将报错,因为反序列化的json中含有实体类不存在的属性:
We’ll solve this by doing our own deserialization with a custom Deserializer:
package com.xiaoxu.test.jackson;
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import java.io.IOException;
/**
* @author xiaoxu
* @date 2022-12-22
* spring_boot:com.xiaoxu.test.jackson.TestCustomizeJackson2
*/
public class TestCustomizeJackson2 {
private static ObjectMapper objectMapper = new ObjectMapper();
static {
objectMapper.configure(SerializationFeature.INDENT_OUTPUT,true);
/* 注册自定义的反序列化器 */
SimpleModule simpleModule = new SimpleModule();
simpleModule.addDeserializer(Good.class, new GoodDeserializer());
objectMapper.registerModule(simpleModule);
}
public static void testStandard2() throws Exception{
String msg = "{\n" +
" \"id\" : 1,\n" +
" \"goodName\" : \"苹果\",\n" +
" \"vipUser\" : {\n" +
" }\n" +
"}";
Good good = objectMapper.readValue(msg, Good.class);
System.out.println("实体类:"+good+"\n");
/* 将反序列化的字符串的key vipUser 改为 normalUser,
* 反序列化将报错*/
String msg2 = "{\n" +
" \"id\" : 1,\n" +
" \"goodName\" : \"苹果\",\n" +
" \"normalUser\" : {\n" +
" \"userName\" : \"tellMe\"\n" +
" }\n" +
"}";
Good good2 = objectMapper.readValue(msg2, Good.class);
System.out.println("实体类2:"+good2+"\n");
}
public static void main(String[] args) throws Exception{
testStandard2();
}
}
class GoodDeserializer extends StdDeserializer<Good>{
protected GoodDeserializer(){
this(null);
}
protected GoodDeserializer(Class<?> vc) {
super(vc);
}
@Override
public Good deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException {
JsonNode jNode = jsonParser.getCodec().readTree(jsonParser);
System.out.println("node: "+jNode);
int id = jNode.get("id").intValue();
String goodName = jNode.get("goodName").asText();
JsonNode normalUser = jNode.get("normalUser");
Good good = new Good();
good.setId(id);
good.setGoodName(goodName);
good.setVipUser(null);
if(null != normalUser){
String normalUserName = jNode.get("normalUser").get("userName").asText();
VIPUser vipUser = new VIPUser();
vipUser.setUid(1000000L);
vipUser.setUserName(normalUserName);
good.setVipUser(vipUser);
return good;
}
return good;
}
}
执行自定义反序列化器,结果如下:
node: {"id":1,"goodName":"苹果","vipUser":{}}
实体类:Good(id=1, goodName=苹果, vipUser=null)
node: {"id":1,"goodName":"苹果","normalUser":{"userName":"tellMe"}}
实体类2:Good(id=1, goodName=苹果, vipUser=VIPUser(uid=1000000, userName=tellMe))
2.3 Custom Deserializer on the Class
区别于2.2的全部配置,可使用注解单独配置。
Alternatively, we can also register the deserializer directly on the class:
再次执行效果一致:
2.4 Custom Deserializer for a Generic Type
Let’s now create a Wrapper class that only contains a unique argument of the generic type T,The User attribute of our Item will now be of type Wrapper< User > instead.
Let’s implement a custom deserializer for this case.
First, we need to implement the ContextualDeserializer interface so that we’ll be able to get the type of the entity inside the Wrapper. We’ll do this by overriding the createContextual() method. When this method is called, the context is resolved and the actual content of the Wrapper can be retrieved via the BeanProperty argument.
We also have to extend JsonDeserializer. Thus, we can set the concrete type of the Wrapper‘s value inside deserialize():
package com.xiaoxu.test.jackson;
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import lombok.Data;
import lombok.ToString;
import java.io.IOException;
import java.lang.annotation.*;
/**
* @author xiaoxu
* @date 2022-12-22
* spring_boot:com.xiaoxu.test.jackson.TestCustomizeJackson3
*/
public class TestCustomizeJackson3 {
private static ObjectMapper objectMapper = new ObjectMapper();
static {
objectMapper.configure(SerializationFeature.INDENT_OUTPUT,true);
/* 注册自定义的序列化器、反序列化器 */
SimpleModule simpleModule = new SimpleModule();
simpleModule.addDeserializer(WrapClazz.class, new WrapperDeserializer());
simpleModule.addSerializer(WrapClazz.class, new WrapperSerializer());
objectMapper.registerModule(simpleModule);
}
public static String buildDto() throws JsonProcessingException {
WrapGood wrapGood = new WrapGood();
wrapGood.setId(111);
wrapGood.setGoodName("美味的东西");
NorUser norUser = new NorUser();
norUser.setUid(555L);
norUser.setUserName("xiaoxu");
WrapClazz<NorUser> wrapClazz = new WrapClazz<>();
wrapClazz.setVal(norUser);
wrapGood.setNorUserWrapClazz(wrapClazz);
/* 因为注册了自定义的序列化器,针对 WrapGood 的 NorUserWrapClazz,
* 将会精确实施序列化 */
String s = objectMapper.writeValueAsString(wrapGood);
System.out.println("序列化的json数据:");
System.out.println(s);
return s;
}
public static void testCustomizeDeserializer() throws JsonProcessingException {
String s = buildDto();
WrapGood wrapGood = objectMapper.readValue(s, WrapGood.class);
System.out.println("反序列化实体类结果:");
System.out.println(wrapGood);
WrapClazz<NorUser> norUserWrapClazz = wrapGood.getNorUserWrapClazz();
System.out.println(norUserWrapClazz);
}
public static void main(String[] args) throws JsonProcessingException {
testCustomizeDeserializer();
}
}
/* 自定义序列化器 */
@SuppressWarnings(value = "rawtypes")
class WrapperSerializer extends JsonSerializer<WrapClazz> implements ContextualSerializer {
@Override
public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
JavaType type = beanProperty.getType();
System.out.println("序列化type:"+type);
return this;
}
@Override
public void serialize(WrapClazz wrapClazz, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
System.out.println("自定义序列化:");
WrapClazz<NorUser> norUserWrapClazz = new WrapClazz<>();
NorUser norUser = new NorUser();
norUser.setUid(666L);
norUser.setUserName("xiaoxuya");
norUserWrapClazz.setVal(norUser);
/* 序列化1: */
// jsonGenerator.writeStartObject();
// jsonGenerator.writeObjectField("xiaoxu",norUser);
// jsonGenerator.writeEndObject();
/* 序列化2:对于泛型对象的序列化,直接使用writeObject,就可以直接写入对象,序列化出来的json,将没有泛型的val这个key */
jsonGenerator.writeObject(norUser);
}
}
/* 自定义反序列化器 */
class WrapperDeserializer extends JsonDeserializer<WrapClazz<?>> implements ContextualDeserializer{
// protected WrapperDeserializer(){
// super();
// }
private JavaType javaType;
@Override
public WrapClazz<?> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException {
System.out.println("当前的类型:"+this.javaType);
WrapClazz<?> wrapClazz = new WrapClazz<>();
wrapClazz.setVal(deserializationContext.readValue(jsonParser, this.javaType));
return wrapClazz;
}
@Override
public JsonDeserializer<?> createContextual(DeserializationContext deserializationContext, BeanProperty beanProperty) throws JsonMappingException {
System.out.println("BeanProperty:"+beanProperty);
/* 获取 WrapGood实体类的 WrapClazz<NorUser> norUserWrapClazz 属性的注解*/
TestMe annotation = beanProperty.getAnnotation(TestMe.class);
if(null != annotation){
System.out.println(annotation.value());
}
JavaType javaType = beanProperty.getType().containedType(0);
System.out.println("反序列化type:"+javaType);
this.javaType = javaType;
return this;
}
}
@ToString
class WrapClazz<T> {
T val;
public T getVal(){
return val;
}
public void setVal(T val){
this.val = val;
}
}
@Data
@ToString
class WrapGood{
public int id;
public String goodName;
@TestMe
WrapClazz<NorUser> norUserWrapClazz;
}
@Data
@ToString
class NorUser{
long uid;
String userName;
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface TestMe{
String value() default "9999";
}
执行结果如下:
序列化type:[simple type, class com.xiaoxu.test.jackson.WrapClazz<com.xiaoxu.test.jackson.NorUser>]
自定义序列化:
序列化的json数据:
{
"id" : 111,
"goodName" : "美味的东西",
"norUserWrapClazz" : {
"uid" : 666,
"userName" : "xiaoxuya"
}
}
BeanProperty:[property 'norUserWrapClazz']
9999
反序列化type:[simple type, class com.xiaoxu.test.jackson.NorUser]
当前的类型:[simple type, class com.xiaoxu.test.jackson.NorUser]
反序列化实体类结果:
WrapGood(id=111, goodName=美味的东西, norUserWrapClazz=WrapClazz(val=NorUser(uid=666, userName=xiaoxuya)))
WrapClazz(val=NorUser(uid=666, userName=xiaoxuya))