java:jackson 二:Custom Deserialization in Jackson

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"}}
实体类2Good(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))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值