目錄html
網上有不少關於jackson和json的介紹和使用,我就不重複造輪子了,本篇博客主要介紹jackson的高級應用和博主我本身踩坑心得。java
若是對json和jackson不熟悉的朋友,能夠看下面兩篇博客。git
https://www.runoob.com/json/json-tutorial.html JSON教程github
https://blog.csdn.net/u011054333/article/details/80504154#commentBox jackson快速入門json
這個註解很是有用,看下面代碼:數組
public class Person { @JsonProperty("username") private String name; private Integer age; //省略getter setter }
@Test public void test1() throws Exception{ ObjectMapper mapper = new ObjectMapper(); Person person = new Person("adai",21); System.out.println(mapper.writeValueAsString(person)); }
輸出爲 {"age":21,"username":"adai"}
app
能夠看到,在序列化的json串中,username替代了nameide
public class Person { @JsonIgnore private String name; private Integer age; //省略getter setter }
@Test public void test1() throws Exception{ ObjectMapper mapper = new ObjectMapper(); Person person = new Person("adai",21); System.out.println(mapper.writeValueAsString(person)); }
輸出爲 {"age":21}
學習
①這個註解和@JsonIgnore有些相似,不過主要是做用在類上面jsonp
@JsonIgnoreProperties(value = {"name","age"}) public class Person { private String name; private Integer age; private Double height; //省略getter setter }
@Test public void test1() throws Exception { ObjectMapper mapper = new ObjectMapper(); Person person = new Person("adai", 21, 172D); String json = mapper.writeValueAsString(person); System.out.println(json); }
輸出爲 {"height":172.0}
能夠看出@JsonIgnoreProperties(value = {"name","age"}) 忽略了name和age屬性,在序列化的時候,會忽略這兩個屬性
②@JsonIgnoreProperties註解還有一個ignoreUnknown屬性,主要用在反序列化上
在正常狀況下,若是咱們json串中有一些key值和咱們的POJO對象不匹配,那麼將會拋出異常。
@Test public void test2() throws Exception { ObjectMapper mapper = new ObjectMapper(); System.out.println(mapper.readValue(" {\"name\":\"adai\",\"age\":21,\"height222\":172.0}", Person.class)); // !!注意height222與咱們的pojo對象不匹配 }
程序將會拋出異常
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "height222" (class com.antiy.common.adai.demo.Person), not marked as ignorable (3 known properties: "name", "age", "height"]) at [Source: (String)"{"name":"adai","age":21,"height222":172.0}"; line: 1, column: 42] (through reference chain: com.antiy.common.adai.demo.Person["height222"])
此時若是咱們在Person類上加上@JsonIgnoreProperties(ignoreUnknown = true)
@JsonIgnoreProperties(ignoreUnknown = true) public class Person { private String name; private Integer age; private Double height; //省略getter setter }
輸出爲 Person(name=adai, age=21, height=null)
③使用 mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); 也能夠達到一樣的目的
④建議:ignoreUnknown和FAIL_ON_UNKNOWN_PROPERTIES儘可能不要設置爲true,若是反序列化的時候,json串中的相關key和POJO屬性不匹配,就讓程序拋出異常,即便發現錯誤,不過具體狀況還須要參考具體業務,jackson默認該值爲false
主要做用:在json串中又包裝了一層
①正常狀況下,序列化的字符串是 {"name":"adai","age":21,"height":172.0}
當咱們在Person
類上加上@@JsonTypeName和@JsonTypeInfo時
@JsonTypeName(value = "user222") @JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) public class Person { private String name; private Integer age; private Double height; //省略getter setter }
輸出爲 {"user222":{"name":"adai","age":21,"height":172.0}}
②咱們也可使用@JsonRootName("user222")和mapper.enable(SerializationFeature.WRAP_ROOT_VALUE)來達到一樣的效果
@JsonRootName("user222") public class Person { private String name; private Integer age; private Double height; //省略getter setter }
@Test public void test1() throws Exception { ObjectMapper mapper = new ObjectMapper(); mapper.enable(SerializationFeature.WRAP_ROOT_VALUE); Person person = new Person("adai", 21, 172D); System.out.println(mapper.writeValueAsString(person)); }
輸出爲 {"user222":{"name":"adai","age":21,"height":172.0}}
主要用在Date屬性上
public class Person { private String name; private Integer age; private Double height; private Date date; //省略getter setter }
@Test public void test1() throws Exception { ObjectMapper mapper = new ObjectMapper(); Person person = new Person("adai", 21, 172D,new Date()); System.out.println(mapper.writeValueAsString(person)); }
輸出爲 {"name":"adai","age":21,"height":172.0,"date":1558842751645}
注意:jackson默認會將Date類型序列化成時間戳,這是由於SerializationFeature中的WRITE_DATES_AS_TIMESTAMPS(true),
該值默認爲true,當咱們手動將改值設爲false時。
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
輸出爲 {"name":"adai","age":21,"height":172.0,"date":"2019-05-26T03:56:38.660+0000"}
這時候date就再也不是時間戳了,可是和咱們中國的時間格式有一些差異,這個時候就可使用@JsonFormat
public class Person { private String name; private Integer age; private Double height; @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss:SSS",timezone="GMT+8") private Date date; //省略getter setter }
輸出爲 {"name":"adai","age":21,"height":172.0,"date":"2019-05-26 11:58:07:296"}
該註解主要用在序列化:
1.方法是非靜態,沒有參數的,方法名隨意
2.方法返回值必須是Map類型
3.在一個實體類中僅僅用在一個方法上
4.序列化的時候json字段的key就是返回Map的key,value就是Map的value
public class Person { private String name; private Integer age; private Map<String, Object> map = new HashMap<>(); public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @JsonAnyGetter // 注意這個註解 public Map<String, Object> getOther(){ return map; } }
@Test public void test1() throws Exception { ObjectMapper mapper = new ObjectMapper(); Person person = new Person(); person.setName("adai"); person.setAge(21); Map<String, Object> other = person.getOther(); other.put("city", "chengdu"); System.out.println(mapper.writeValueAsString(person)); }
輸出爲 {"name":"adai","age":21,"city":"chengdu"}
當咱們在public Map<String, Object> getOther()
上去掉@JsonAnyGetter
這個註解的時候
輸出爲 {"name":"adai","age":21,"other":{"city":"chengdu"}}
能夠看出加上這個註解之後序列化的時候就會將Map裏面的值也至關於實體類裏面的字段給顯示出來了。
主要做用於反序列化上
1.用在非靜態方法上,註解的方法必須有兩個參數,第一個是json字段中的key,第二個是value,方法名隨意
2.反序列化的時候將對應不上的字段所有放到Map裏面
public class Person { private String name; private Integer age; private Map<String, String> map = new HashMap<>(); public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @JsonAnySetter //注意這個註解 public void setOther(String key, String value){ this.map.put(key, value); } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", map=" + map + '}'; } }
@Test public void test1() throws Exception { ObjectMapper mapper = new ObjectMapper(); String json = "{\"name\":\"adai\",\"age\":21,\"color\":\"red\",\"city\":12}"; Person person = mapper.readValue(json, Person.class); System.out.println(person); }
輸出爲 Person{name='adai', age=21, map={color=red, city=12}}
能夠看出,使用@JsonAnySetter註解,在json串中多餘的屬性會被自動放在map屬性中,而不會拋出UnrecognizedPropertyException異常
注意:若是是Map<String,String> 那麼即便是 {"name":"adai","age":21,"city":12,"weather":true}
中的city對應數值 12
和weather對應布爾 true
也會被封裝進Map<String, String>中,可是Map<String, Integer> 沒法封裝String或其餘類型,只能封裝Integer
Java中 List和Map主要和泛型打交道,咱們重點以這兩個爲例子,來學習jackson中如何在反序列中保留泛型信息的。
public class Student { private String name; private Integer age; //省略getter setter }
@Test public void test3() throws Exception { ObjectMapper mapper = new ObjectMapper(); List<Student> list = new ArrayList<>(); list.add(new Student("adai",21)); list.add(new Student("apei",22)); String json = mapper.writeValueAsString(list); List<Student> student = mapper.readValue(json, List.class); System.out.println(student.get(0).getName()); }
該程序在編譯期不會報錯,能夠執行。那麼在運行期的時候能夠經過嗎?
答案是:否認的。 即程序運行失敗
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.antiy.common.adai.demo.Student
緣由①:由於在反序列化的時候,mapper.readValue(json, List.class)
並無告訴jackson,這個json數據能夠封裝成Student對象,因此jackson默認將[{"name":"adai","age":21},{"name":"apei","age":22}]
封裝成兩個LinkedHashMap對象,而後放入到List集合中。
緣由②:既然咱們知道了List中保存的對象在運行期是LinkedHashMap,那麼爲何在代碼中還能夠student.get(0).getName()
,這就跟Java編譯期的泛型擦除有關係了,咱們能夠看下反編譯後的代碼
List<Student> student = (List)mapper.readValue(json, List.class); System.out.println(((Student)student.get(0)).getName());
student.get(0)實際上的對象是LinkedHashMap,而後強轉成Student,天然就報錯了!
咱們可使用JavaType
來保存泛型信息
List:
@Test public void test4() throws Exception { ObjectMapper mapper = new ObjectMapper(); JavaType javaType = mapper.getTypeFactory().constructParametricType(List.class, Student.class); List<Student> list = new ArrayList<>(); list.add(new Student("adai",21)); list.add(new Student("apei",22)); String json = mapper.writeValueAsString(list); List<Student> student2 = mapper.readValue(json, javaType); System.out.println(student2.get(0).getName()); }
輸出爲 adai
Map:
@Test public void test5() throws Exception { ObjectMapper mapper = new ObjectMapper(); JavaType javaType = mapper.getTypeFactory().constructParametricType(Map.class, String.class, Student.class); // 第二個參數是Map的key,第三個參數是Map的value Map<String, Student> map = new HashMap<>(); map.put("first",new Student("adai",21)); map.put("second",new Student("apei",22)); String json = mapper.writeValueAsString(map); Map<String, Student> result = mapper.readValue(json, javaType); System.out.println(result.get("first").getName()); }
輸出爲 adai
TypeReference
比javaType
模式更加方便,代碼也更加簡潔
List:
@Test public void test6() throws Exception { ObjectMapper mapper = new ObjectMapper(); List<Student> list = new ArrayList<>(); list.add(new Student("adai",21)); list.add(new Student("apei",22)); String json = mapper.writeValueAsString(list); List<Student> student2 = mapper.readValue(json, new TypeReference<List<Student>>(){}); System.out.println(student2.get(0).getName()); }
輸出爲 adai
Map:
@Test public void test7() throws Exception { ObjectMapper mapper = new ObjectMapper(); Map<String, Student> map = new HashMap<>(); map.put("first",new Student("adai",21)); map.put("second",new Student("apei",22)); String json = mapper.writeValueAsString(map); Map<String, Student> result = mapper.readValue(json, new TypeReference<Map<String,Student>>(){}); System.out.println(result.get("first").getName()); }
輸出爲 adai
能夠看到,使用TypeReference
,只須要在mapper.readValue後面增長一個 new TypeReference
匿名內部類,寫上本身想要封裝的泛型對象,比javaType
少了一行mapper.getTypeFactory().constructParametricType
聲明
jackson能夠經過SerializationFeature
和DeserializationFeature
來自定義,序列化和反序列化規則,這也是jackson很是強大的地方。
請看下面一個例子:
mapper.configure(SerializationFeature.INDENT_OUTPUT,true); mapper.enable(SerializationFeature.INDENT_OUTPUT); mapper.disable(SerializationFeature.INDENT_OUTPUT);
這裏有三個方法,configure方法接受配置名和要設置的值,Jackson 2.5版本新加的enable和disable方法則直接啓用和禁用相應屬性,我推薦使用後面兩個方法。
默認爲false,該屬性主要是美化json輸出
普通序列化的json串:
{"name":"adai","age":21}
開啓該屬性後的json串:
{ "name" : "adai", "age" : 21 }
默認爲true,該屬性的意思是,若是一個對象中沒有任何的屬性,那麼在序列化的時候就會報錯
public class Teacher {}
@Test public void test1() throws Exception { ObjectMapper mapper = new ObjectMapper(); Teacher teacher = new Teacher(); System.out.println(mapper.writeValueAsString(teacher)); }
程序運行將會報錯:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class com.antiy.common.adai.entity.Teacher and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
當咱們進行設置: mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
輸出爲 {}
默認爲true,該屬性的意思是,jackson默認會將Date類型的數據序列化成時間戳
詳情能夠參考 2.5 @JsonFormat
默認爲true,該屬性的意思是,在反序列的時候,若是json串中存在一些key,可是在POJO中沒有,那麼程序將會拋出異常
@Test public void test2() throws Exception { ObjectMapper mapper = new ObjectMapper(); Student student = new Student("adai",21); String json = "{\"name\":\"adai\",\"age222\":21}"; //Student中沒有age222 mapper.readValue(json,Student.class); }
程序將會報錯:UnrecognizedPropertyException: Unrecognized field "age222"
此時咱們將FAIL_ON_UNKNOWN_PROPERTIES
設置爲false
public void test2() throws Exception { ObjectMapper mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); Student student = new Student("adai",21); String json = "{\"name\":\"adai\",\"age222\":21}"; System.out.println(mapper.readValue(json, Student.class)); }
輸出爲 Student(name=adai, age=null)
該值默認爲false,該屬性的意思是,容許JSON空字符串值(「」)做爲null綁定到POJO的屬性上,看代碼可能比較好理解一點。
public class Teacher { private Student student; // 省略 getter setter constructor }
@Test public void test2() throws Exception { ObjectMapper mapper = new ObjectMapper(); String json = "{\"student\":\"\"}"; System.out.println(mapper.readValue(json, Teacher.class)); }
程序將會報錯,MismatchedInputException
,由於json串中key值student對應的value爲 ""
此時咱們能夠設置DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT
爲true
輸出爲 Teacher(student=null)
"" 空串 被轉換成null值 封裝到Teacher對象的student屬性中
默認爲false,該屬性的意思是,將內容包裹爲一個JSON屬性,屬性名由@JsonRootName註解指定。
詳情請見 2.4 @JsonTypeName和@JsonTypeInfo
必定要導入正確的TypeReference
類
注意,該屬性只接受POJO的 「」 空字符串轉換成 null,在json中,String很是特殊。
請先看4.6章節的內容。
此時我將Teacher中的student類型,換成String
public class Teacher { private String student; }
@Test public void test2() throws Exception { ObjectMapper mapper = new ObjectMapper(); String json = "{\"student\":\"\"}"; System.out.println(mapper.readValue(json, Teacher.class)); }
輸出爲 Teacher(student=)
原來覺得,若是是String屬性,那麼""也會轉換成null,結果偏偏相反,只有POJO對象,「」纔會轉換成null
參考 stackoverflow:https://stackoverflow.com/questions/22688713/jackson-objectmapper-deserializationconfig-feature-accept-empty-string-as-null-o
在對象序列化和反序列化的過程當中,本身對Map和List又有了新的理解。
Map能夠當作是一個任意對象,保存字段屬性。
在 3.1中,若是jackson不知道反序列化的對象,那麼jackson將會以LinkedHashMap來進行處理,這正是由於Map的 Key-Value 特性。
@Test public void test2() throws Exception { ObjectMapper mapper = new ObjectMapper(); Map<String, Object> map = new HashMap<>(2); map.put("name","adai"); map.put("age",21); System.out.println("map序列化: " + mapper.writeValueAsString(map)); Student student = new Student("adai",21); System.out.println("student序列化: " + mapper.writeValueAsString(student)); }
輸出爲 map序列化: {"name":"adai","age":21}
student序列化: {"name":"adai","age":21}
能夠看到Map和Student序列化的結果都是同樣的,那麼在反序列化的時候,能夠用Student對象接受的數據,天然而然也能夠用Map接收,這就是爲何在關於泛型反序列化的時候,若是jackson不知道具體的對象,所有都會用LinkHashMap接收
List就當作是一個數組
參考資料:
https://github.com/FasterXML/jackson/
http://www.javashuo.com/article/p-zbvvzltn-a.html
https://www.ibm.com/developerworks/cn/java/jackson-advanced-application/index.html
http://www.manongjc.com/article/114528.html
https://www.baeldung.com/jackson-object-mapper-tutorial