• Jackson自定义序列化


    本篇简单写写Jackson中自定义序列化的方式。

    假设有类CountryLeader,分别表示国家以及国家领导人,领导人除了姓名外还有年龄和民族,定义如下

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Country {
        private String countryName;
        private Leader leader;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Leader {
        private String leaderName;
        private Integer age;
        private String nation;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    基于上述定义,且看下面的代码输出结果结果

    @Test
    void test5() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        Country country = new Country("元朝", new Leader("忽必烈", 79, "蒙古族"));
        System.out.println(objectMapper.writeValueAsString(country));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    输出结果

    {"countryName":"元朝","leader":{"leaderName":"忽必烈","age":79,"nation":"蒙古族"}}
    
    • 1

    可以看到,正常的序列化方式会将leader的属性都进行序列化。

    然而,有些时候,我们并不需要关心这个国家的领导人的详细信息,我们只需要知道他叫什么名字即可,例如我们想在序列化之后输入如下的JSON,那就需要定制序列化方式。

    1、自定义序列化器

    首先定义一个序列化器,继承自Serializer类或其子类

    import com.fasterxml.jackson.core.JsonGenerator;
    import com.fasterxml.jackson.databind.SerializerProvider;
    import com.fasterxml.jackson.databind.ser.std.StdSerializer;
    import java.io.IOException;
    
    public class LeaderSerializer extends StdSerializer<Leader> {
        public LeaderSerializer() {
            this(null);
        }
    
        protected LeaderSerializer(Class<Leader> t) {
            super(t);
        }
    
        @Override
        public void serialize(Leader value, JsonGenerator gen, SerializerProvider provider) throws IOException {
            if (value == null) {
                gen.writeNull();
                return;
            }
            // 直接输入领导人姓名,不需要起始和结束的花括号
            gen.writeString(value.getLeaderName());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    有了序列化,自然需要反序列化

    import com.fasterxml.jackson.core.JsonParser;
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.DeserializationContext;
    import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
    import java.io.IOException;
    
    public class LeaderDeserializer extends StdDeserializer<Leader> {
        public LeaderDeserializer() {
            this(null);
        }
    
        protected LeaderDeserializer(Class<?> vc) {
            super(vc);
        }
    
        @Override
        public Leader deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
            String leaderName = p.getValueAsString();
            if (leaderName == null) {
                return null;
            }
            return getLeaderByName(leaderName);
        }
    
        private Leader getLeaderByName(String leaderName) {
            // TODO 查询数据库或通过其他操作返回leader详细信息
            if ("忽必烈".equals(leaderName)) {
                Leader leader = new Leader();
                leader.setLeaderName(leaderName);
                leader.setAge(79);
                leader.setNation("蒙古族");
                return leader;
            }
            return null;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    然后应用序列化类,在leader属性上添加序列化和反序列化注解。

    注意,不能添加在Leader类上,否则会改变Leader类的默认序列化和反序列化方式,这里序列化的目标依旧是Country

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Country {
        private String countryName;
    
        @JsonSerialize(using = LeaderSerializer.class)
        @JsonDeserialize(using = LeaderDeserializer.class)
        private Leader leader;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    测试

    @Test
    void test6() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        Leader leader = new Leader("忽必烈", 79, "蒙古族");
        Country country = new Country("元朝", leader);
        System.out.println(objectMapper.writeValueAsString(leader));
        System.out.println(objectMapper.writeValueAsString(country));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    输出结果

    {"leaderName":"忽必烈","age":79,"nation":"蒙古族"}
    {"countryName":"元朝","leader":"忽必烈"}
    
    • 1
    • 2

    从输出结果可以看到,Leader的序列化方式依旧是正常的,但是Country中的leader属性序列化后输出的只有leaderName属性,已经实现了我们需要的功能。

    再来测试反序列化

    @Test
    void test7() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        String json = "{\"countryName\":\"元朝\",\"leader\":\"忽必烈\"}";
        System.out.println(objectMapper.readValue(json, Country.class));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    输出结果

    Country(countryName=元朝, leader=Leader(leaderName=忽必烈, age=79, nation=蒙古族))
    
    • 1

    可以看到,结果也是正常的。

    2、使用@JsonValue@JsonCreator注解

    首先将上面Country中leader属性上加的序列化注解和反序列化注解去掉,还原为最初,然后修改Leader类如下

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Leader {
    
        @JsonValue
        private String leaderName;
    
        private Integer age;
        private String nation;
    
        @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
        public static Leader getLeaderByName(String leaderName) {
            // TODO 查询数据库或通过其他操作返回leader详细信息
            if ("忽必烈".equals(leaderName)) {
                Leader leader = new Leader();
                leader.setLeaderName(leaderName);
                leader.setAge(79);
                leader.setNation("蒙古族");
                return leader;
            }
            return null;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    注意:@JsonCreator注解所标注的类必须要是static的

    再来执行上面的测试类test6(),输出结果如下

    "忽必烈"
    {"countryName":"元朝","leader":"忽必烈"}
    
    • 1
    • 2

    对比序列化器,可以看到,使用@JsonValue注解已经将Leader类的序列化方式改变了,进而影响了Country类。再来执行test7()测试反序列化,结果与之前是一致的。但是执行下面的反序列化测试的时候却报了错,从报错信息可以看到是JSON识别错误,暂时还未找到解决方法,待后续补充完善。

    @Test
    void test8() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        System.out.println(objectMapper.readValue("忽必烈", Leader.class));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    com.fasterxml.jackson.core.JsonParseException: Unrecognized token '忽必烈': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')
     at [Source: (String)"忽必烈"; line: 1, column: 4]
    
    • 1
    • 2

    所以更推荐使用自定义序列化器和反序列化器的方式来实现。

  • 相关阅读:
    还是不够快?几个方法帮你加快Python运行速度
    8. 列表
    ROS-Unity连接教程
    Lactoferrin-PEG-alginate 乳铁蛋白-聚乙二醇-海藻酸钠
    解决主机无法通过网络访问虚拟机的问题
    推进智慧工地建设,智慧工地是什么?建筑工地人必看!
    什么是jvm
    Intel汇编语言程序设计(第7版)第四章编程练习题答案
    移动端测试的学习
    (附源码)springboot校园兼职系统 毕业设计 463450
  • 原文地址:https://blog.csdn.net/Remember_Z/article/details/127947500