前言

  Spring Boot提供了与三个JSON映射库的集成:

  • Gson
  • Jackson
  • JSON-B

  Jackson是首选的默认库。

  官网介绍:

  https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/html/boot-features-json.html#boot-features-json-jackson

  https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/html/howto-spring-mvc.html#howto-customize-the-jackson-objectmapper

  通常,我们将Java对象转成Json时称之为序列化,反之将Json转成Java对象时称之为反序列化,本文简单介绍一下Jackson,以及在SpringBoot项目开发中常用的Jackson方法

  如何引入

  SpringBoot提供了JSON依赖,我们可以按下面方式引入

  1、直接引入JSON依赖

    <!-- springboot-json -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>

  2、一般情况下我们引入MVC,MVC里面帮我们引入了JSON依赖

        <!-- springboot web(MVC)-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

  最终引入的依赖是

  Jackson注解

  Jackson的注解详细介绍

  英文官方介绍:https://github.com/FasterXML/jackson-annotations

  常用注解

  @JsonProperty 序列化、反序列化时,属性的名称

  @JsonIgnoreProperties 序列化、反序列化忽略属性,多个时用“,”隔开

  @JsonIgnore  序列化、反序列化忽略属性

  @JsonAlias  为反序列化期间要接受的属性定义一个或多个替代名称,可以与@JsonProperty一起使用

  @JsonInclude 当属性的值为空(null或者"")时,不进行序列化,可以减少数据传输

  @JsonFormat 序列化、反序列化时,格式化时间

  测试

  完整测试案例:

@Data
//序列化、反序列化忽略的属性,多个时用“,”隔开
@JsonIgnoreProperties({"captcha"})
//当属性的值为空(null或者"")时,不进行序列化,可以减少数据传输
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class UserVo { // 序列化、反序列化时,属性的名称
@JsonProperty("userName")
private String username; // 为反序列化期间要接受的属性定义一个或多个替代名称,可以与@JsonProperty一起使用
@JsonAlias({"pass_word", "passWord"})
@JsonProperty("pwd")
private String password; //序列化、反序列化时,格式化时间
@JsonFormat(locale = "zh", timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private Date createDate; //序列化、反序列化忽略属性
@JsonIgnore
private String captcha; }

  写一个controller测试一下

  先写一个页面跳转

    /**
* 跳转页面,页面引入了jquery,主要用于下面的ajax调用测试
*/
@GetMapping("/")
public ModelAndView index(){
return new ModelAndView("index");
}

  使用@RestController标注类,相对于所有的方法都用@ResponseBody标注,MVC会帮我们调用序列化,将Java对象转成Json再响应给调用方,同时形参要加@RequestBody标注,MVC会帮我们调用反序列化将Json转成Java对象,这就要求我们调用的时候需要传一个Json字符串过来

@RestController
@RequestMapping("/")
public class TestContrller {/*
$.ajax({
type:"POST",
url:"http://localhost:1099/testByJson",
data:JSON.stringify({
userName:"sa",
pass_word:"123fff",
captcha:"abcd",
createDate:"2019-08-05 11:34:31"
}),
dataType:"JSON",
contentType:"application/json;charset=UTF-8",
success:function(data){
console.log(data);
},
error:function(data){
console.log("报错啦");
}
})
*/
@PostMapping("testByJson")
public UserVo testByJson(@RequestBody UserVo userVo){
System.out.println(userVo);
return userVo;
}
}

  

  调用测试

  1、先注释所有注解,仅打开这个两个类上面的注解@JsonIgnoreProperties、@JsonInclude

@Data
//序列化、反序列化忽略的属性,多个时用“,”隔开
@JsonIgnoreProperties({"captcha"})
//当属性的值为空(null或者"")时,不进行序列化,可以减少数据传输
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class UserVo { // 序列化、反序列化时,属性的名称
// @JsonProperty("userName")
private String username; // 为反序列化期间要接受的属性定义一个或多个替代名称,可以与@JsonProperty一起使用
// @JsonAlias({"pass_word", "passWord"})
// @JsonProperty("pwd")
private String password; //序列化、反序列化时,格式化时间
// @JsonFormat(locale = "zh", timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private Date createDate; //序列化、反序列化忽略属性
// @JsonIgnore
private String captcha; }

UserVo

  前端调用时全部按属性名称

   data:JSON.stringify({
username:"sa",
password:"123fff",
captcha:"abcd"
})

  反序列化(后端控制台打印)

UserVo(username=sa, password=123fff, createDate=null, captcha=null)

  序列化(ajax的回调)

{username: "sa", password: "123fff"}

  captcha属性前端已经传值,但设置了@JsonIgnoreProperties注解反序列化时该属性被忽略,因此为空,而序列化的时候@JsonInclude配置的是JsonInclude.Include.NON_EMPTY,当属性的值为空(null或者"")时,不进行序列化,所以序列化的最终结果如上所示

  2、先注释所有注解,放开@JsonProperty、@JsonAlias、@JsonIgnore

@Data
//序列化、反序列化忽略的属性,多个时用“,”隔开
//@JsonIgnoreProperties({"captcha"})
//当属性的值为空(null或者"")时,不进行序列化,可以减少数据传输
//@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class UserVo { // 序列化、反序列化时,属性的名称
@JsonProperty("userName")
private String username; // 为反序列化期间要接受的属性定义一个或多个替代名称,可以与@JsonProperty一起使用
@JsonAlias({"pass_word", "passWord"})
@JsonProperty("pwd")
private String password; //序列化、反序列化时,格式化时间
// @JsonFormat(locale = "zh", timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private Date createDate; //序列化、反序列化忽略属性
@JsonIgnore
private String captcha; }

UserVo

  前端调用还是按属性名称

   data:JSON.stringify({
username:"sa",
password:"123fff",
captcha:"abcd"
})

  反序列化(后端控制台打印)

UserVo(username=null, password=null, createDate=null, captcha=null)

  序列化(ajax的回调)

{createDate: null, userName: null, pwd: null}

  captcha被@JsonIgnore标注,序列化、反序列忽略它,username、password被@JsonProperty标注,传参的时候只能用别名,password同时被@JsonAlias标注,可以用代替名称

  因此我们可以这样调用

   data:JSON.stringify({
userName:"sa",
pass_word:"123fff",
//以下两种也一样
//passWord:"123fff",
//pwd:"123fff",
captcha:"abcd"
})

  反序列化(后端控制台打印)

UserVo(username=sa, password=123fff, createDate=null, captcha=null)

  序列化(ajax的回调)

{userName: "sa", pwd: "123fff"}

  

  3、先注释所有注解,放开@JsonFormat

@Data
//序列化、反序列化忽略的属性,多个时用“,”隔开
//@JsonIgnoreProperties({"captcha"})
//当属性的值为空(null或者"")时,不进行序列化,可以减少数据传输
//@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class UserVo { // 序列化、反序列化时,属性的名称
// @JsonProperty("userName")
private String username; // 为反序列化期间要接受的属性定义一个或多个替代名称,可以与@JsonProperty一起使用
// @JsonAlias({"pass_word", "passWord"})
// @JsonProperty("pwd")
private String password; //序列化、反序列化时,格式化时间
@JsonFormat(locale = "zh", timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private Date createDate; //序列化、反序列化忽略属性
// @JsonIgnore
private String captcha; }

UserVo

  前端调用

   data:JSON.stringify({
createDate:"2019-08-05 11:34:31"
})

  反序列化(后端控制台打印)

UserVo(username=null, password=null, createDate=Mon Aug 05 11:34:31 GMT+08:00 2019, captcha=null)

  序列化(ajax的回调)

{username: null, password: null, createDate: "2019-08-05 11:34:31", captcha: null}

  PS:没有配置之前这样调用会报错400

Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.util.Date` from String "2019-08-05 11:34:31": not a valid representation (error: Failed to parse Date value '2019-08-05 11:34:31': Cannot parse date "2019-08-05 11:34:31": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSSZ', parsing fails (leniency? null)); nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.util.Date` from String "2019-08-05 11:34:31": not a valid representation (error: Failed to parse Date value '2019-08-05 11:34:31': Cannot parse date "2019-08-05 11:34:31": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSSZ', parsing fails (leniency? null))

  如果不是以反序列化的方式注入,而是MVC的方式注入又是怎么样呢?去掉@RequestBody就变成MVC注入

    /*
$.ajax({
type:"GET",
url:"http://localhost:1099/testByMvc",
data:{
username:"sa",
password:"123fff",
captcha:"abcd"
},
dataType:"JSON",
contentType:"application/json;charset=UTF-8",
success:function(data){
console.log(data);
},
error:function(data){
console.log("报错啦");
}
})
*/
@GetMapping("testByMvc")
public UserVo testByMvc(UserVo userVo){
System.out.println(userVo);return userVo;
}

  放开所有注释

@Data
//序列化、反序列化忽略的属性,多个时用“,”隔开
@JsonIgnoreProperties({"captcha"})
//当属性的值为空(null或者"")时,不进行序列化,可以减少数据传输
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class UserVo { // 序列化、反序列化时,属性的名称
@JsonProperty("userName")
private String username; // 为反序列化期间要接受的属性定义一个或多个替代名称,可以与@JsonProperty一起使用
@JsonAlias({"pass_word", "passWord"})
@JsonProperty("pwd")
private String password; //序列化、反序列化时,格式化时间
@JsonFormat(locale = "zh", timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private Date createDate; //序列化、反序列化忽略属性
@JsonIgnore
private String captcha; }

UserVo

  MVC注入的时候,接参过程Jackson的注解就不再生效了,这时候我们传参就得按照MVC的规则来,Date类型首先就不能传字符串

  前端调用

           data:{
username:"sa",
password:"123fff",
captcha:"abcd"
}

  后台打印

UserVo(username=sa, password=123fff, createDate=null, captcha=abcd)

  ajax回调,由于还是使用了@RestController,所有MVC会帮我们调用序列化再响应回去

{userName: "sa", pwd: "123fff"}

  那MVC方式注入,Date日期类型该怎么支持传字符串呢?在配置文件新增MVC日期格式化就可以愉快的传输固定格式的日期字符串了

#MVC接参时,日期处理
spring.mvc.date-format=yyyy-MM-dd HH:mm:ss

  (偷个懒,效果与预期一样,就贴图了。。。)

  同时,不管是采用哪种注入方法,我们可以配置全局的日期处理,这样一来就可以愉快开发了

#全局日期格式化处理

#MVC接参时,日期处理
spring.mvc.date-format=yyyy-MM-dd HH:mm:ss #Jackson序列化、反序列化时,日期处理
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss

  我们顺便来看一下在配置文件都有哪些Jackson配置,每个配置的具体功能见名思意,就不阐述了

  ObjectMapper

  以上都是配置注解,具体操作都是MVC帮我们做了,那我们如何使用Jackson进行Json操作呢?我们在官方文档可以看到Jackson为我们提供了com.fasterxml.jackson.databind.ObjectMapper类操作Json

  官方文档相关介绍:https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/html/howto-spring-mvc.html#howto-customize-the-jackson-objectmapper

  常用方法

    public static void main(String[] args) {
try {
ObjectMapper mapper = new ObjectMapper(); //当属性的值为空(null或者"")时,不进行序列化,可以减少数据传输
mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); //设置日期格式
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); //1、Java对象转Json字符串
UserVo userVo = new UserVo();
userVo.setUsername("张三");
userVo.setPassword("666");
String jsonString = mapper.writeValueAsString(userVo);
System.out.println(jsonString); //2、Json字符串转Java对象
jsonString = "{\"userName\":\"张三\"}";
UserVo userVo1 = mapper.readValue(jsonString, UserVo.class);
System.out.println(userVo1); //3、Java对象类型转换
HashMap<Object, Object> map = new HashMap<>();
map.put("userName", "张三");
UserVo userVo2 = mapper.convertValue(map, UserVo.class);
System.out.println(userVo2); //4、将json字符串转换成List
String listJsonString = "[{\"userName\":\"张三\"},{\"userName\":\"李四\"}]";
List<UserVo> userVoList = mapper.readValue(listJsonString, mapper.getTypeFactory().constructParametricType(List.class, UserVo.class));
System.out.println(userVoList); } catch (IOException e) {
e.printStackTrace();
}
}

  还有一些不怎么常用的方法,比如下面这几个(除了转成Json字符串)

  后记

  通常,实体类用于ORM映射框架与数据打交道,比如:User,要求对象的属性要与数据库字段一一对应,少了不行,多了也不行,没有对应映射的得用注解标注(比如JPA),所以我们一般用Vo对象进行传输、接参等,会多很多乱七八糟的属性(分页信息,仅用于接参的临时属性等),比如:UserVo,User、UserVo两个对象使用工具类相互转换,有时候Vo对象有些乱七八糟的属性不想进行序列化传输,就需要设置序列化过滤

  在SpringBoot中使用Jackson操作Json序列化、反序列化的简单操作就暂时记录到这,以后再继续补充

  补充

  2019-10-22补充:不同时区,时间序列化处理

  需求:要求系统根据当前登录账号存储的时区字段,web端显示对应时区的时间

  通常情况下,系统会分为svc端服务、web端服务,svc服务负责与数据库打交道,web服务负责与浏览器打交道;因此,我们可以在svc服务数据存库的时候统一存储GMT+0000,web服务序列化响应的时候根据当前登录账户时区进行显示,简单来说就是:web端服务根据当前登录人的时区来显示日期时间,但svc端服务日期入库统一采用GMT+0000时区。

  实现:

  svc端服务,在系统启动时设置全局默认GMT+0000时区

@SpringBootApplication
public class XXXApplication {
public static void main(String[] args) {
//设置全局默认时区
TimeZone.setDefault(TimeZone.getTimeZone("GMT+0000"));
SpringApplication.run(XXXApplication .class, args);
}
}

  web端服务,设置自定义JsonSerializer<Date>日期序列化实现类,在实现类中获取登录账户时区,设置序列化日期格式

@JsonComponent
public class WebDateFormat { //SimpleDateFormat对象
private static final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); @Autowired
private LoginService loginService; //格式化日期
public static class DateFormatSerializer extends JsonSerializer<Date> {
@Override
public void serialize(Date value, JsonGenerator gen, SerializerProvider serializers) {
try {
//获取登录账号时区字段,并设置序列化日期格式
String timeZone = loginService.getLoginUser().getTimeZone();
format.setTimeZone(TimeZone.getTimeZone(timeZone));
gen.writeString(format.format(value)); } catch (IOException e) {
throw new RuntimeException(e);
}
}
} //解析日期字符串
public static class DateParseDeserializer extends JsonDeserializer<Date> {
@Override
public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
try {
//获取登录账号时区字段,并设置序列化日期格式
String timeZone = loginService.getLoginUser().getTimeZone();
format.setTimeZone(TimeZone.getTimeZone(timeZone));
return format.parse(p.getValueAsString()); } catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
}

  代码开源

  代码已经开源、托管到我的GitHub、码云:

  GitHub:https://github.com/huanzi-qch/springBoot

  码云:https://gitee.com/huanzi-qch/springBoot

SpringBoot系列——Jackson序列化的更多相关文章

  1. 源码分析springboot自定义jackson序列化,默认null值个性化处理返回值

    最近项目要实现一种需求,对于后端返回给前端的json格式的一种规范,不允许缺少字段和字段值都为null,所以琢磨了一下如何进行将springboot的Jackson序列化自定义一下,先看看如何实现,再 ...

  2. SpringBoot系列——Spring-Data-JPA

    前言 jpa是ORM映射框架,更多详情,请戳:apring-data-jpa官网:http://spring.io/projects/spring-data-jpa,以及一篇优秀的博客:https:/ ...

  3. SpringBoot系列: RestTemplate 快速入门

    ====================================相关的文章====================================SpringBoot系列: 与Spring R ...

  4. springboot之jackson的两种配置方式

    springboot 针对jackson是自动化配置的,如果需要修改,有两种方式: 方式一:通过application.yml 配置属性说明:## spring.jackson.date-format ...

  5. springBoot系列-->springBoot注解大全

    一.注解(annotations)列表 @SpringBootApplication:包含了@ComponentScan.@Configuration和@EnableAutoConfiguration ...

  6. SpringBoot系列之Spring Data Jpa集成教程

    SpringBoot系列之Spring Data Jpa集成教程 Spring Data Jpa是属于Spring Data的一个子项目,Spring data项目是一款集成了很多数据操作的项目,其下 ...

  7. SpringBoot系列教程JPA之新增记录使用姿势

    SpringBoot系列教程JPA之新增记录使用姿势 上一篇文章介绍了如何快速的搭建一个JPA的项目环境,并给出了一个简单的演示demo,接下来我们开始业务教程,也就是我们常说的CURD,接下来进入第 ...

  8. SpringBoot 系列教程之事务隔离级别知识点小结

    SpringBoot 系列教程之事务隔离级别知识点小结 上一篇博文介绍了声明式事务@Transactional的简单使用姿势,最文章的最后给出了这个注解的多个属性,本文将着重放在事务隔离级别的知识点上 ...

  9. SpringBoot系列之RabbitMQ使用实用教程

    SpringBoot系列之RabbitMQ使用实用教程 @ 目录 1. 消息队列概述 1.1 MQ的概述 1.2 MQ目的地形式 2. 消息队列实现方式 2.1 常见MQ框架 2.2 MQ实现方式 3 ...

随机推荐

  1. 告别编码5分钟,命名2小时!史上最全的Java命名规范参考!

    简洁清爽的代码风格应该是大多数工程师所期待的.在工作中笔者常常因为起名字而纠结,夸张点可以说是编程5分钟,命名两小时!究竟为什么命名成为了工作中的拦路虎. 每个公司都有不同的标准,目的是为了保持统一, ...

  2. JavaEE基础(01):Servlet实现方式,生命周期执行过程

    本文源码:GitHub·点这里 || GitEE·点这里 一.Servlet简介 Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容.使用S ...

  3. Ubuntu 18.04 LTS上安装NFS服务器和客户端

    NFS是基于UDP/IP协议的应用,其实现主要是采用远程过程调用RPC机制,RPC提供了一组与机器.操作系统以及低层传送协议无关的存取远程文件的操作.RPC采用了XDR的支持.XDR是一种与机器无关的 ...

  4. Magicodes.Sms短信库的封装和集成

    简介 Magicodes.Sms是心莱团队封装的短信服务库,已提供Abp模块的封装. Nuget 新的包 名称 说明 Nuget Magicodes.Sms.Aliyun 阿里云短信库 Magicod ...

  5. [TimLinux] docker CentOS7安装docker-ce最新版

    1. 环境 $ lsb_release -a # 需要安装 redhat-lsb-core 包 LSB Version: :core-4.1-amd64:core-4.1-noarch Distrib ...

  6. Codeforces Round #595 (Div. 3) D2Too Many Segments,线段树

    题意:给n个线段,每个线段会覆盖一些点,求删最少的线段,使得每个点覆盖的线段不超过k条. 思路:按右端点排序,之后依次加入每个线段,查询线段覆盖区间内的每个点,覆盖的最大线段数量,如果不超过k,那就可 ...

  7. oracle 日常巡检

    1. 检查数据库基本状况 包含:检查Oracle实例状态,检查Oracle服务进程,检查Oracle监听进程,共三个部分. 1.1. 检查Oracle实例状态 select instance_name ...

  8. Vue使用Viser

    最近产品经理发现antV的图表非常漂亮,想在项目里使用,看了下文档antV适用于Vue的分支叫Viser,Viser的官方文档写的有点随意,在此给出Vue中使用Viser的方法如下: 1.安装Vise ...

  9. PHP ftp获取目录内容为空

    使用PHP的ftp函数获取目录内容,ftp_nlist()和ftp_rawlist()返回都为空. 查了一圈资料找不到答案,然后用Python写了一个,一样的操作就可以获取目录内容. 抓包发现,Pyt ...

  10. Appium移动端自动化测试--控件定位方法

    常用定位手段 id Accessibility ID XPath 控件基础知识 DOM: Document Object Model文档对象模型 DOM应用:最早应用于HTML和Javascript的 ...