jackson序列化和反序列化的应用实践指南

源码地址:https://github.com/zhouweixin/serializable

1 相关概念

序列化: 把对象转换为字节序列的过程称为对象的序列化

反序列化: 把字节序列恢复为对象的过程称为对象的反序列化

2 序列化的作用

用于把内存中的对象状态保存到一个文件中或者数据库中

用于网络传送对象

用于远程调用传输对象

3 准备序列化对象

准备了两个类, 教师类和学生类, 其中一个学生只有一个教师

这里省略了构造方法和setter, getter方法

Teacher.java

public class Teacher {
 private String name;
 private Integer age;
}

Student.java

package org.zwx;

public class Student {
 private String name;
 private Integer age;
 private Sex sex;
 private String fatherName;
 private Date bornTime;
 private Teacher teacher;
}

Sex.java

public enum Sex {
 MALE("男"), FEMALE("女");

 private String name;

 Sex(String name) {
 this.name = name;
 }

 public String getName() {
 return name;
 }
}

4 引入jackson依赖

本示例是基于gradle的, 从maven中心仓库中选择了2.11.2版本的jackson-databind

compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.11.2'

5 序列化与格式化输出

5.1 流程

首先需要有一个待序列化对象, 本例中的student对象

创建一个对象映射器, jackson包下的ObjectMapper

调用序列化函数, 本例中的writeValueAsString, 将对象转为字符串, 便于展示

5.2 代码

public void testSerializable() throws IOException {
 Student student1 = new Student("小明", 18, Sex.MALE, "王富贵", new Date(), new Teacher("李老师", 40));
 Student student2 = new Student("小花", 16, Sex.FEMALE, "钱很多", new Date(), new Teacher("赵老师", 38));
 List<Student> students = new ArrayList<>();
 students.add(student1);
 students.add(student2);

 ObjectMapper mapper = new ObjectMapper();
 String s = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(students);
 System.out.println(s);
}

5.3 结果

[ {
 "name" : "小明",
 "age" : 18,
 "sex" : "MALE",
 "fatherName" : "王富贵",
 "bornTime" : 1599996926917,
 "teacher" : {
 "name" : "李老师",
 "age" : 40
 }
}, {
 "name" : "小花",
 "age" : 16,
 "sex" : "FEMALE",
 "fatherName" : "钱很多",
 "bornTime" : 1599996926917,
 "teacher" : {
 "name" : "赵老师",
 "age" : 38
 }
} ]

5.4 分析

示例中调用了方法writerWithDefaultPrettyPrinter, 美化了json的格式

否则将打印

[{"name":"小明","age":18,"sex":"MALE","fatherName":"王富贵","bornTime":1599997061097,"teacher":{"name":"李老师","age":40}},{"name":"小花","age":16,"sex":"FEMALE","fatherName":"钱很多","bornTime":1599997061097,"teacher":{"name":"赵老师","age":38}}]

6 自定义序列化的名字

6.1 场景

假如需要将序列化的json由驼峰命名修改为下划线命名, 如fatherName修改为father_name

只需要在字段fatherName上用注解JsonProperty配置

6.2 示例代码

@JsonProperty("father_name")
private String fatherName;
@JsonProperty("born_time")
private Date bornTime;

6.3 示例结果

[ {
  "name" : "小明",
  "age" : 18,
  "sex" : "MALE",
  "teacher" : {
    "name" : "李老师",
    "age" : 40
  },
  "father_name" : "王富贵",
  "born_time" : 1599997157609
}, {
  "name" : "小花",
  "age" : 16,
  "sex" : "FEMALE",
  "teacher" : {
    "name" : "赵老师",
    "age" : 38
  },
  "father_name" : "钱很多",
  "born_time" : 1599997157610
} ]

7 自定义输出格式

7.1 bornTime格式设置

当前bornTime的格式为unix时间戮, 可读性非常差

现修改为yyyy-MM-dd HH:mm:ss

并设置时区为东八区

示例代码

@JsonProperty("born_time")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date bornTime;

结果

[ {
  "name" : "小明",
  "age" : 18,
  "sex" : "MALE",
  "teacher" : {
    "name" : "李老师",
    "age" : 40
  },
  "father_name" : "王富贵",
  "born_time" : "2020-09-13 19:50:47"
}, {
  "name" : "小花",
  "age" : 16,
  "sex" : "FEMALE",
  "teacher" : {
    "name" : "赵老师",
    "age" : 38
  },
  "father_name" : "钱很多",
  "born_time" : "2020-09-13 19:50:47"
} ]

7.2 sex设置为中文

只需要为Sex添加一个方法getOrdinal, 并添加注解JsonValue即可

示例代码

@JsonValue
public String getOrdinal() {
 return name;
}

示例结果

[ {
  "name" : "小明",
  "age" : 18,
  "sex" : "男",
  "teacher" : {
    "name" : "李老师",
    "age" : 40
  },
  "father_name" : "王富贵",
  "born_time" : "2020-09-13 19:57:47"
}, {
  "name" : "小花",
  "age" : 16,
  "sex" : "女",
  "teacher" : {
    "name" : "赵老师",
    "age" : 38
  },
  "father_name" : "钱很多",
  "born_time" : "2020-09-13 19:57:47"
} ]

7.3 sex设置为序号

有些场景喜欢用0和1等序号设置男女, 即枚举的序号: 0表示男, 1表示女

此时需要修改Set的getOrdinal方法

  1. 修改返回值类型为int
  2. 调用父类的getOrdinal方法

示例代码

@JsonValue
public int getOrdinal() {
 return super.ordinal();
}

示例结果

[ {
  "name" : "小明",
  "age" : 18,
  "sex" : 0,
  "teacher" : {
    "name" : "李老师",
    "age" : 40
  },
  "father_name" : "王富贵",
  "born_time" : "2020-09-13 20:01:44"
}, {
  "name" : "小花",
  "age" : 16,
  "sex" : 1,
  "teacher" : {
    "name" : "赵老师",
    "age" : 38
  },
  "father_name" : "钱很多",
  "born_time" : "2020-09-13 20:01:44"
} ]

8 拍平嵌套类型

场景

如前面提到的结果所示, teacher的两个属性并不在student的第一层,
有时可能会更深的层次, 使用起来不太友好

如何用teacher_name和teacher_age两个属性代替teacher呢?

  1. 在Student的teacher属性上添加注解JsonUnwrapped, 意为不包裹
  2. 在Teacher的属性上利用注解JsonProperty重命名

示例代码

Student.java

@JsonUnwrapped
private Teacher teacher;

Teacher.java

@JsonProperty("teacher_name")
private String name;
@JsonProperty("teacher_age")
private Integer age;

示例结果

[ {
  "name" : "小明",
  "age" : 18,
  "sex" : 0,
  "teacher_name" : "李老师",
  "teacher_age" : 40,
  "father_name" : "王富贵",
  "born_time" : "2020-09-13 20:21:53"
}, {
  "name" : "小花",
  "age" : 16,
  "sex" : 1,
  "teacher_name" : "赵老师",
  "teacher_age" : 38,
  "father_name" : "钱很多",
  "born_time" : "2020-09-13 20:21:53"
} ]

9 自定义序列化器

9.1 场景

假如需要将年龄调整为理论学龄, 即将年龄减去7, 得到理论学龄, 如何操作呢?

创建自定义年龄序列化器AgeSerializer, 继承StdSerializer<>

  1. 创建AgeSerializer的构造方法
  2. 重写serialize函数

利用注解修指定Student属性age的序列化器AgeSerializer

9.2 示例代码

AgeSerializer.java

public class AgeSerializer extends StdSerializer<Integer> {
 protected AgeSerializer() {
 super(Integer.class);
 }

 @Override
 public void serialize(Integer value, JsonGenerator gen, SerializerProvider provider) throws IOException {
 gen.writeNumber(value - 7);
 }
}

Student.java

@JsonSerialize(using = AgeSerializer.class)
private Integer age;

9.3 示例结果

[ {
  "name" : "小明",
  "age" : 11,
  "sex" : 0,
  "teacher_name" : "李老师",
  "teacher_age" : 40,
  "father_name" : "王富贵",
  "born_time" : "2020-09-13 20:31:59"
}, {
  "name" : "小花",
  "age" : 9,
  "sex" : 1,
  "teacher_name" : "赵老师",
  "teacher_age" : 38,
  "father_name" : "钱很多",
  "born_time" : "2020-09-13 20:31:59"
} ]

10 反序列化

10.1 流程

首先需要有序列化好的数据, 可以是string, byte[], 文件二进制等

创建一个对象映射器, jackson包下的ObjectMapper

调用反序列化函数, 本例中的readValue, 将字符串转为对象

10.2 反序列化对象数据

示例代码

public void testDeserializable() throws JsonProcessingException {
 String s = "{\"name\":\"小明\",\"age\":11,\"sex\":0,\"teacher_name\":\"李老师\",\"teacher_age\":40,\"father_name\":\"王富贵\",\"born_time\":\"2020-09-13 20:46:10\"}";
 ObjectMapper mapper = new ObjectMapper();
 Student student = mapper.readValue(s, Student.class);
 System.out.println(student);
}

示例结果

Student{name='小明', age=11, sex=MALE, fatherName='王富贵', bornTime=Sun Sep 13 20:46:10 CST 2020, teacher=Teacher{name='李老师', age=40}}

分析

为了便于打印对象数据, 重写了Student和Teacher的toString方法

从数据中可以看出, age的结果是错误的, 原因在于之前自定义的序列化器将年龄减小了7, 10.4节将会通过自定义反序列化器来解决此问题

10.3 反序列化对象数组数据

示例代码

public void testDeserializableStudents() throws JsonProcessingException {
 String s = "[{\"name\":\"小明\",\"age\":11,\"sex\":0,\"teacher_name\":\"李老师\",\"teacher_age\":40,\"father_name\":\"王富贵\",\"born_time\":\"2020-09-13 20:51:31\"},{\"name\":\"小花\",\"age\":9,\"sex\":1,\"teacher_name\":\"赵老师\",\"teacher_age\":38,\"father_name\":\"钱很多\",\"born_time\":\"2020-09-13 20:51:31\"}]";
 ObjectMapper mapper = new ObjectMapper();
 Student[] students = mapper.readValue(s, Student[].class);
 for (Student student : students) {
 System.out.println(student);
 }
}

示例结果

Student{name='小明', age=11, sex=MALE, fatherName='王富贵', bornTime=Sun Sep 13 20:51:31 CST 2020, teacher=Teacher{name='李老师', age=40}}
Student{name='小花', age=9, sex=FEMALE, fatherName='钱很多', bornTime=Sun Sep 13 20:51:31 CST 2020, teacher=Teacher{name='赵老师', age=38}}

分析

readValue的第二个参数需要传类型, 这里推荐用数组, 不推荐用List, 具体原因笔者目前也没花时间去研究

10.4 自定义反序列化器

从10.2节及10.3的现象中可以看出来, 仅仅自定义的序列化器会导致序列化的过程是正常的, 反序列化的过程仍然是默认逻辑, 有时候会导致意想不到的结果

遇到此场景, 可以考虑自定义反序列化器

  1. 创建自定义反序列化器AgeDeserializer, 继承StdDeserializer<>
  2. 重写deserialize方法
  3. 在Student的age属性上添加注解JsonDeserialize, 并指定反序列化器AgeDeserializer

示例代码

AgeDeserializer.java

public class AgeDeserializer extends JsonDeserializer<Integer> {
 @Override
 public Integer deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
 return p.getIntValue() + 7;
 }
}

Student.java

@JsonSerialize(using = AgeSerializer.class)
@JsonDeserialize(using = AgeDeserializer.class)
private Integer age;

示例结果

Student{name='小明', age=18, sex=MALE, fatherName='王富贵', bornTime=Sun Sep 13 20:51:31 CST 2020, teacher=Teacher{name='李老师', age=40}}
Student{name='小花', age=16, sex=FEMALE, fatherName='钱很多', bornTime=Sun Sep 13 20:51:31 CST 2020, teacher=Teacher{name='赵老师', age=38}}

11 注解JsonInclude

该注解使用在实体类上, 格式@JsonInclude(value = JsonInclude.Include.NON_DEFAULT)

其中, Include有7种参数, 功能对比如下

参数 功能 备注
Include.ALWAYS 属性总是序列化(需要有get方法) 默认值
Include.NON_DEFAULT 属性为默认值不序列化 如: int:0, bool:false
Include.NON_EMPTY 属性为空("")或null不序列化
Include.NON_NULL 属性为null不序列化
Include.CUSTOM
Include.USE_DEFAULTS
Include.NON_ABSENT

代码示例

Student.java

@JsonInclude(value = JsonInclude.Include.NON_DEFAULT)
public class Student {
public void testNonDefault() throws IOException {
 Student student = new Student("", 0, null, null, null, null);
 ObjectMapper mapper = new ObjectMapper();
 String s = mapper.writeValueAsString(student);
 System.out.println(s);
}

示例输出

{
  "name" : "",
  "age" : -7
}

分析

当属性为默认值, 即零值时, 不序列化

常见的零值:

  1. int: 0
  2. bool: false,
  3. String: null

12 注解Transient

13 注解JsonIgnore

总结

到此这篇关于jackson序列化和反序列化的应用实践指南的文章就介绍到这了,更多相关jackson序列化和反序列化内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java下利用Jackson进行JSON解析和序列化示例

    Java下常见的Json类库有Gson.JSON-lib和Jackson等,Jackson相对来说比较高效,在项目中主要使用Jackson进行JSON和Java对象转换,下面给出一些Jackson的JSON操作方法. 一.准备工作 首先去官网下载Jackson工具包.Jackson有1.x系列和2.x系列,截止目前2.x系列的最新版本是2.2.3,2.x系列有3个jar包需要下载: jackson-core-2.2.3.jar(核心jar包,下载地址) jackson-annotations-2

  • Jackson优雅序列化Java枚举类过程解析

    1. 前言 在Java开发中我们为了避免过多的魔法值,使用枚举类来封装一些静态的状态代码.但是在将这些枚举的意思正确而全面的返回给前端却并不是那么顺利,我们通常会使用Jackson类库序列化对象为JSON,今天就来讲一个关于使用Jackson序列化枚举的通用性技巧. 2. 通用枚举范式 为了便于统一处理和规范统一的风格,建议指定一个统一的抽象接口,例如: /** * The interface Enumerator. */ public interface Enumerator { /** *

  • jackson 实体转json 为NULL或者为空不参加序列化(实例讲解)

    使用jackson进行序列化时,往往会遇到后台某个实体对象的属性为null,当序列化成json时对应的属性也为null:这样在某些前端组件上应用该json对象会报错.(例如:echarts) 下面总结了两种方法,解决了当属性为null时不参与序列化: 方法一: 1.实体上使用如下注解 @JsonInclude(Include.NON_NULL) 将该标记放在属性上,如果该属性为NULL则不参与序列化 :如果放在类上边,那对这个类的全部属性起作用. 具体取值有: //Include.Include

  • 如何利用Jackson序列化忽略指定类型的属性详解

    前言 本文准确来讲是探讨如何用 Jackson 来序列化 Apache avro 对象,因为简单用 Jackson 来序列化 Apache avro 对象会报错.原因是序列化 Schema getSchema() 时会报错,后面会讲到,需要序列化时忽略该属性.那么能不能在 getSchema() 上加上 @JsonIgnore 来忽略该属性呢?原理上是通的.不过手工修改的 avsc 生成的 Java 文件随时会因为重新编译而还原,所以不太具有实际可操作性,当然通过定制编译 avsc 用的模板文件

  • SpringBoot2.0整合jackson配置日期格式化和反序列化的实现

    网上杂七杂八的说法不一,大多数都是抄来抄去,没有实践,近期在项目频繁遇到boot+jackson处理日期的问题,故开此贴. 首先是POM <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance

  • 解析Java的Jackson库中对象的序列化与数据泛型绑定

    Jackson对象序列化 这里将介绍将Java对象序列化到一个JSON文件,然后再读取JSON文件获取转换为对象.在这个例子中,创建了Student类.创建将有学生对象以JSON表示在一个student.json文件. 创建一个名为JacksonTester在Java类文件在 C:\>Jackson_WORKSPACE. 文件: JacksonTester.java import java.io.File; import java.io.IOException; import org.codeh

  • java如何利用FastJSON、Gson、Jackson三种Json格式工具自定义时间序列化

    Java处理JSON数据有三个比较流行的类库FastJSON.Gson和Jackson. Jackson Jackson是由其社区进行维护,简单易用并且性能也相对高些.但是对于复杂的bean转换Json,转换的格式鄙视标准的Json格式.PS:Jackson为Spring MVC内置Json解析工具 Gson Gson是由谷歌公司研发的产品,目前是最全的Json解析工具.完全可以将复杂的类型的Json解析成Bean或者Bean到Json的转换 FastJson Fastjson是一个Java语言

  • jackson序列化和反序列化的应用实践指南

    源码地址:https://github.com/zhouweixin/serializable 1 相关概念 序列化: 把对象转换为字节序列的过程称为对象的序列化 反序列化: 把字节序列恢复为对象的过程称为对象的反序列化 2 序列化的作用 用于把内存中的对象状态保存到一个文件中或者数据库中 用于网络传送对象 用于远程调用传输对象 3 准备序列化对象 准备了两个类, 教师类和学生类, 其中一个学生只有一个教师 这里省略了构造方法和setter, getter方法 Teacher.java publ

  • Jackson序列化和反序列化忽略字段操作

    一.设置Jackson序列化时只包含不为空的字段 new ObjectMapper().setSerializationInclusion(Include.NON_NULL); 二.设置在反序列化时忽略在JSON字符串中存在,而在Java中不存在的属性 new ObjectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); 三.Jackson序列化时忽略字段的方式 1.方式一:FilterProvider a)在需

  • 如何自定义Jackson序列化 @JsonSerialize

    目录 自定义Jackson序列化 @JsonSerialize jackson自定义全局序列化.反序列化 创建序列化类 创建反序列化类 将两个类注册进入jackson核心对象objectMapper 小结一下 自定义Jackson序列化 @JsonSerialize 自定义json序列化需要实现StdSerializer<T>或者JsonSerializer<T>. 我要序列化House这个类,加上注解,指定用于序列化的类 package com.xhx.json.entity;

  • springboot jackson自定义序列化和反序列化实例

    目录 jackson自定义序列化和反序列 序列化JsonSerializer 最后请求http返回对象 反序列化JsonDeserializer 小结一下 springboot 自定义序列化器 jackson自定义序列化和反序列 spingmvc使用httpmessageconverter接口来转换http请求和响应. 如果需要添加和自定义转换器,则可以使用spring boot的HttpMessageConverters类:任何存在于上下文中的HttpMessageConverter的ben都

  • SpringMVC Json自定义序列化和反序列化的操作方法

    需求背景 需求一:SpringMVC构建的微服务系统,数据库对日期的存储是Long类型的时间戳,前端之前是默认使用Long类型时间,现在前端框架改动,要求后端响应数据时,Long类型的时间自动变成标准时间格式(yyyy-MM-dd HH:mm:ss). 涉及到这个转换的范围挺大,所有的实体表都有创建时间createTime和修改时间updateTime,目前的主要诉求也是针对这两个字段,并且在实体详情数据和列表数据都存在,需要一个统一的方法,对这两个字段进行处理. 需求二:前端请求上传的JSON

  • SpringBoot中时间类型 序列化、反序列化、格式处理示例代码

    [SpringBoot] 中时间类型 序列化.反序列化.格式处理 Date yml全局配置 spring: jackson: time-zone: GMT+8 date-format: yyyy-MM-dd HH:mm:ss #配置POST请求Body中Date时间类型序列化格式处理,并返回 请求参数类型转换 /** * 时间Date转换 * 配置GET请求,Query查询Date时间类型参数转换 */ @Component public class DateConverter implemen

  • 基于Spring Web Jackson对RequestBody反序列化失败的解决

    最近在用Spring Web做一些Restful API的实现试验,碰到了@RequestBody 的JSON无法被正常反序列化的问题.服务端的代码大致如下: @RequestMapping(value = "/aquas", method = RequestMethod.POST) public Aqua createAqua(@RequestBody Aqua aqua) { return aqua; } 研究后发现,由于Aqua这个实体类缺少了默认的无参构造函数(如果自行添加了有

  • Jackson序列化丢失泛型的解决

    Jackson序列化丢失泛型 经过 项目中遇到一个奇怪的bug,即一个Map<Integer,List<Integer>>的泛型map,向map中get一个存在的key,事实上却返回null. 经过排查,发现是该map被Jackson序列化后,key的类型从Integer变成了String类型.再经过反序列化,即使已经声明key泛型的Integer,反序列化后内存数据中的key为String并不是Integer类型且并未抛出异常. 复现 1.声明一个key泛型为Integer的ma

  • 关于jackson序列化和feign返回值的问题

    目录 jackson序列化和feign返回值 jackson注意点 feignClient返回值问题 feign调用异常,反序列化失败 异常消息如下 jackson序列化和feign返回值 jackson注意点 被序列化/反序列化的实体 a.必须要有无参构造方法 b.字段要有set/get方法 c.不需要序列化的字段 可以用@jsonIgnore 修饰 feignClient 返回值问题 返回值的类型 不一定 和被调用方返回值类型一样 如: 被调用方是 @ResponseBody @Reques

  • SpringBoot之Json的序列化和反序列化问题

    目录 控制json序列化/反序列化 1. @JsonIgnoreProperties的用法 2. @JsonProperty 注解 3. @JsonCreator 注解 4. @JsonSetter 和 @JsonGetter 注解 5. @JsonAnySetter 注解 6. @JsonAnyGetter 注解 7. @JsonFormat 注解 8.@JsonSerialize 和 @JsonDeserialize 注解 SpringBoot序列化规则 1.全局设置 2.局部设置 3.自定

随机推荐