SpringBoot系列——Jackson序列化

  前言

  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依赖

1<!-- springboot-json --> 2 <dependency> 3 <groupId>org.springframework.boot</groupId> 4 <artifactId>spring-boot-starter-json</artifactId> 5 </dependency>

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

1<!-- springboot web(MVC)--> 2 <dependency> 3 <groupId>org.springframework.boot</groupId> 4 <artifactId>spring-boot-starter-web</artifactId> 5 </dependency>

  最终引入的依赖是

  Jackson注解

  Jackson的注解详细介绍

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

  常用注解

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

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

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

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

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

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

  测试

  完整测试案例:

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

  写一个controller测试一下

  先写一个页面跳转

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

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

1@RestController 2@RequestMapping("/") 3public class TestContrller {/* 4 $.ajax({ 5 type:"POST", 6 url:"http://localhost:1099/testByJson", 7 data:JSON.stringify({ 8 userName:"sa", 9 pass_word:"123fff", 10 captcha:"abcd", 11 createDate:"2019-08-05 11:34:31" 12 }), 13 dataType:"JSON", 14 contentType:"application/json;charset=UTF-8", 15 success:function(data){ 16 console.log(data); 17 }, 18 error:function(data){ 19 console.log("报错啦"); 20 } 21 }) 22 */ 23 @PostMapping("testByJson") 24 public UserVo testByJson(@RequestBody UserVo userVo){ 25 System.out.println(userVo); 26 return userVo; 27 } 28}

  调用测试

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

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

UserVo

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

1 data:JSON.stringify({ 2 username:"sa", 3 password:"123fff", 4 captcha:"abcd" 5 })

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

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

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

UserVo

  前端调用还是按属性名称

1 data:JSON.stringify({ 2 username:"sa", 3 password:"123fff", 4 captcha:"abcd" 5 })

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

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

  序列化(ajax的回调)

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

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

  因此我们可以这样调用

1 data:JSON.stringify({ 2 userName:"sa", 3 pass_word:"123fff", 4 //以下两种也一样 5 //passWord:"123fff", 6 //pwd:"123fff", 7 captcha:"abcd" 8 })

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

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

  序列化(ajax的回调)

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

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

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

UserVo

  前端调用

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

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

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注入

1/* 2 $.ajax({ 3 type:"GET", 4 url:"http://localhost:1099/testByMvc", 5 data:{ 6 username:"sa", 7 password:"123fff", 8 captcha:"abcd" 9 }, 10 dataType:"JSON", 11 contentType:"application/json;charset=UTF-8", 12 success:function(data){ 13 console.log(data); 14 }, 15 error:function(data){ 16 console.log("报错啦"); 17 } 18 }) 19 */ 20 @GetMapping("testByMvc") 21 public UserVo testByMvc(UserVo userVo){ 22 System.out.println(userVo);return userVo; 23 }

  放开所有注释

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

UserVo

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

  前端调用

1 data:{ 2 username:"sa", 3 password:"123fff", 4 captcha:"abcd" 5 }

  后台打印

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

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

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

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

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

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

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

1#全局日期格式化处理 2 3#MVC接参时,日期处理 4spring.mvc.date-format=yyyy-MM-dd HH:mm:ss 5 6#Jackson序列化、反序列化时,日期处理 7spring.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

  常用方法

1public static void main(String[] args) { 2 try { 3 ObjectMapper mapper = new ObjectMapper(); 4 5 //当属性的值为空(null或者"")时,不进行序列化,可以减少数据传输 6 mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); 7 8 //设置日期格式 9 mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); 10 11 //1、Java对象转Json字符串 12 UserVo userVo = new UserVo(); 13 userVo.setUsername("张三"); 14 userVo.setPassword("666"); 15 String jsonString = mapper.writeValueAsString(userVo); 16 System.out.println(jsonString); 17 18 //2、Json字符串转Java对象 19 jsonString = "{\"userName\":\"张三\"}"; 20 UserVo userVo1 = mapper.readValue(jsonString, UserVo.class); 21 System.out.println(userVo1); 22 23 //3、Java对象类型转换 24 HashMap<Object, Object> map = new HashMap<>(); 25 map.put("userName", "张三"); 26 UserVo userVo2 = mapper.convertValue(map, UserVo.class); 27 System.out.println(userVo2); 28 29 //4、将json字符串转换成List 30 String listJsonString = "[{\"userName\":\"张三\"},{\"userName\":\"李四\"}]"; 31 List<UserVo> userVoList = mapper.readValue(listJsonString, mapper.getTypeFactory().constructParametricType(List.class, UserVo.class)); 32 System.out.println(userVoList); 33 34 } catch (IOException e) { 35 e.printStackTrace(); 36 } 37 }

   还有一些不怎么常用的方法,比如下面这几个(除了转成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时区

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

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

1@JsonComponent 2public class WebDateFormat { 3 4 //SimpleDateFormat对象 5 private static final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); 6 7 @Autowired 8 private LoginService loginService; 9 10 //格式化日期 11 public static class DateFormatSerializer extends JsonSerializer<Date> { 12 @Override 13 public void serialize(Date value, JsonGenerator gen, SerializerProvider serializers) { 14 try { 15 //获取登录账号时区字段,并设置序列化日期格式 16 String timeZone = loginService.getLoginUser().getTimeZone(); 17 format.setTimeZone(TimeZone.getTimeZone(timeZone)); 18 gen.writeString(format.format(value)); 19 20 } catch (IOException e) { 21 throw new RuntimeException(e); 22 } 23 } 24 } 25 26 //解析日期字符串 27 public static class DateParseDeserializer extends JsonDeserializer<Date> { 28 @Override 29 public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { 30 try { 31 //获取登录账号时区字段,并设置序列化日期格式 32 String timeZone = loginService.getLoginUser().getTimeZone(); 33 format.setTimeZone(TimeZone.getTimeZone(timeZone)); 34 return format.parse(p.getValueAsString()); 35 36 } catch (ParseException e) { 37 throw new RuntimeException(e); 38 } 39 } 40 } 41}

  代码开源

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

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

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

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )