Java的Jackson自定义序列化详解

 更新时间:2023年11月06日 10:17:25   作者:杨某人信了你的邪  
这篇文章主要介绍了Java的Jackson自定义序列化详解,对比序列化器,可以看到,使用@JsonValue注解已经将Leader类的序列化方式改变了,进而影响了Country类,再来执行test7()测试反序列化,结果与之前是一致的,需要的朋友可以参考下

Jackson自定义序列化

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

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

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Country {
    private String countryName;
    private Leader leader;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Leader {
    private String leaderName;
    private Integer age;
    private String nation;
}

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

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

输出结果

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

可以看到,正常的序列化方式会将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());
    }
}

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

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;
    }
}

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

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

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Country {
    private String countryName;

    @JsonSerialize(using = LeaderSerializer.class)
    @JsonDeserialize(using = LeaderDeserializer.class)
    private Leader leader;
}

测试

@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));
}

输出结果

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

从输出结果可以看到,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));
}

输出结果

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

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

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;
    }

}

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

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

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

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

@Test
void test8() throws JsonProcessingException {
    ObjectMapper objectMapper = new ObjectMapper();
    System.out.println(objectMapper.readValue("忽必烈", Leader.class));
}
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]

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

到此这篇关于Java的Jackson自定义序列化详解的文章就介绍到这了,更多相关Jackson自定义序列化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java实现状态模式的示例代码

    Java实现状态模式的示例代码

    状态模式是一种行为型设计模式,允许对象根据其内部状态改变行为,本文主要介绍了Java实现状态模式的示例代码,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧
    2025-02-02
  • Vue3实现多页面跳转效果的几种方式

    Vue3实现多页面跳转效果的几种方式

    Vue.js是一个用于构建用户界面的渐进式 JavaScript 框架,它提供了多种方法来实现页面之间的导航,在 Vue 3 中,页面跳转主要通过 Vue Router 来管理,同时也支持其他方式如编程式导航和使用锚点链接,本文将详细介绍 Vue 3 中的各种页面跳转方式,需要的朋友可以参考下
    2025-03-03
  • springboot+mybatis快速插入大量数据的具体实现

    springboot+mybatis快速插入大量数据的具体实现

    最近导入表格数据时需要同时插入修改大量数据,下面这篇文章主要给大家介绍了关于springboot+mybatis快速插入大量数据的具体实现,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-04-04
  • Springboot通过谷歌Kaptcha 组件生成图形验证码功能

    Springboot通过谷歌Kaptcha 组件生成图形验证码功能

    Kaptcha是谷歌开源的一款简单实用的图形验证码组件。我个人推荐它的最大原因是容易上手,采用约定大于配置的方式,快速契合到项目中,这篇文章主要介绍了Springboot通过谷歌Kaptcha组件生成图形验证码的方法,需要的朋友可以参考下
    2023-05-05
  • SpringCloud中NacosNamingService的作用详解

    SpringCloud中NacosNamingService的作用详解

    这篇文章主要介绍了SpringCloud中NacosNamingService的作用详解,NacosNamingService类完成服务实例注册,撤销与获取服务实例操作,NacosNamingService初始化采用单例模式,使用反射生成,需要的朋友可以参考下
    2023-11-11
  • Java实现计算机程序设计思路

    Java实现计算机程序设计思路

    这篇文章主要为大家介绍了Java实现计算机程序设计思路,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-11-11
  • java使用CollectionUtils工具类判断集合是否为空方式

    java使用CollectionUtils工具类判断集合是否为空方式

    这篇文章主要介绍了java使用CollectionUtils工具类判断集合是否为空方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-02-02
  • java long 类型数据的赋值方式

    java long 类型数据的赋值方式

    这篇文章主要介绍了java long 类型数据的赋值方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • 浅谈java Collection中的排序问题

    浅谈java Collection中的排序问题

    下面小编就为大家带来一篇浅谈java Collection中的排序问题。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-12-12
  • Springboot中使用缓存的示例代码

    Springboot中使用缓存的示例代码

    这篇文章主要介绍了Springboot中使用缓存的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-09-09

最新评论