Spring Boot提供了與三個JSON映射庫的集成:html
Jackson是首選的默認庫。前端
官網介紹:java
一般,咱們將Java對象轉成Json時稱之爲序列化,反之將Json轉成Java對象時稱之爲反序列化,本文簡單介紹一下Jackson,以及在SpringBoot項目開發中經常使用的Jackson方法github
SpringBoot提供了JSON依賴,咱們能夠按下面方式引入web
一、直接引入JSON依賴ajax
<!-- springboot-json --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-json</artifactId> </dependency>
二、通常狀況下咱們引入MVC,MVC裏面幫咱們引入了JSON依賴spring
<!-- springboot web(MVC)--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
最終引入的依賴是數據庫
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; } }
調用測試
一、先註釋全部註解,僅打開這個兩個類上面的註解@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; }
前端調用時所有按屬性名稱
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或者"")時,不進行序列化,因此序列化的最終結果如上所示
二、先註釋全部註解,放開@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; }
前端調用仍是按屬性名稱
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"}
三、先註釋全部註解,放開@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; }
前端調用
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; }
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配置,每一個配置的具體功能見名思意,就不闡述了
以上都是配置註解,具體操做都是MVC幫咱們作了,那咱們如何使用Jackson進行Json操做呢?咱們在官方文檔能夠看到Jackson爲咱們提供了com.fasterxml.jackson.databind.ObjectMapper類操做Json
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")); //一、Java對象轉Json字符串 UserVo userVo = new UserVo(); userVo.setUsername("張三"); userVo.setPassword("666"); String jsonString = mapper.writeValueAsString(userVo); System.out.println(jsonString); //二、Json字符串轉Java對象 jsonString = "{\"userName\":\"張三\"}"; UserVo userVo1 = mapper.readValue(jsonString, UserVo.class); System.out.println(userVo1); //三、Java對象類型轉換 HashMap<Object, Object> map = new HashMap<>(); map.put("userName", "張三"); UserVo userVo2 = mapper.convertValue(map, UserVo.class); System.out.println(userVo2); //四、將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、碼雲: