官方WIKI:https://github.com/FasterXML/jackson-databind/wiki
jackson 1.x和2.x版本的注解是放置在不同的包下的
1.x是在jackson core jar包org.codehaus.jackson.annotate下
2.x是在jackson-databind包com.fasterxml.jackson.annotation下
一.jackson的自動檢測機制
jackson允許使用任意的構造方法或工廠方法來構造實例
使用@JsonAutoDetect(作用在類上)來開啟/禁止自動檢測
fieldVisibility:字段的可見級別
ANY:任何級別的字段都可以自動識別
NONE:所有字段都不可以自動識別
NON_PRIVATE:非private修飾的字段可以自動識別
PROTECTED_AND_PUBLIC:被protected和public修飾的字段可以被自動識別
PUBLIC_ONLY:只有被public修飾的字段才可以被自動識別
DEFAULT:同PUBLIC_ONLY
jackson默認的字段屬性發現規則如下:
所有被public修飾的字段->所有被public修飾的getter->所有被public修飾的setter
舉例:
public static class TestPOJO{ TestPOJO(){} TestPOJO(String name){ this.name = name; } private String name; @Override public String toString() { return "TestPOJO{" + "name='" + name + '\'' + '}'; } }
這個類我們只有一個private的name屬性,並且沒有提供對應的get,set方法,如果按照默認的屬性發現規則我們將無法序列化和反序列化name字段(如果沒有get,set方法,只有被public修飾的屬性才會被發現),你可以通過修改@JsonAutoDetect的fieldVisibility來調整自動發現級別,為了使name被自動發現,我們需要將級別調整為ANY。
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
同理,除了fieldVisibility可以設置外,還可以設置getterVisibility、setterVisibility、isGetterVisibility、creatorVisibility級別,不再多講。
除了上面的方式,你還可以有一些其他方式可以配置methods,fields和creators(構造器和靜態方法)的自動檢測,例如:
你可以配置MapperFeature來啟動/禁止一些特別類型(getters,setters,fields,creators)的自動檢測,比如下面的MapperFeature配置:
SORT_PROPERTIES_ALPHABETICALLY:按字母順序排序屬性
ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY,true);
二.配置SerializationFeature
一些我們比較常用的SerializationFeature配置:
1.SerializationFeature.WRAP_ROOT_VALUE
是否環繞根元素,默認false,如果為true,則默認以類名作為根元素,你也可以通過@JsonRootName來自定義根元素名稱
objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE,true);
舉例:
@JsonRootName("myPojo") public static class TestPOJO{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
該類在序列化成json后類似如下:{"myPojo":{"name":"aaaa"}}。
2.SerializationFeature.INDENT_OUTPUT
是否縮放排列輸出,默認false,有些場合為了便於排版閱讀則需要對輸出做縮放排列。
舉例:如果一個類中有a、b、c、d四個可檢測到的屬性,那么序列化后的json輸出類似下面:
{ "a" : "aaa", "b" : "bbb", "c" : "ccc", "d" : "ddd" }
3.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS
序列化日期時以timestamps輸出,默認true
比如如果一個類中有private Date date;這種日期屬性,序列化后為:{"date" : 1413800730456},若不為true,則為{"date" : "2014-10-20T10:26:06.604+0000"}
4.SerializationFeature.WRITE_ENUMS_USING_TO_STRING
序列化枚舉是以toString()來輸出,默認false,即默認以name()來輸出
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING,true);
5.SerializationFeature.WRITE_ENUMS_USING_INDEX
序列化枚舉是以ordinal()來輸出,默認false
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_INDEX,true);
舉例:
@Test public void enumTest() throws Exception { TestPOJO testPOJO = new TestPOJO(); testPOJO.setName("myName"); testPOJO.setMyEnum(TestEnum.ENUM01); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING,false); objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_INDEX,false); String jsonStr1 = objectMapper.writeValueAsString(testPOJO); Assert.assertEquals("{\"myEnum\":\"ENUM01\",\"name\":\"myName\"}",jsonStr1); ObjectMapper objectMapper2 = new ObjectMapper(); objectMapper2.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING,true); String jsonStr2 = objectMapper2.writeValueAsString(testPOJO); Assert.assertEquals("{\"myEnum\":\"enum_01\",\"name\":\"myName\"}",jsonStr2); ObjectMapper objectMapper3 = new ObjectMapper(); objectMapper3.configure(SerializationFeature.WRITE_ENUMS_USING_INDEX,true); String jsonStr3 = objectMapper3.writeValueAsString(testPOJO); Assert.assertEquals("{\"myEnum\":0,\"name\":\"myName\"}",jsonStr3); } public static class TestPOJO{ TestPOJO(){} private TestEnum myEnum; private String name; //getters、setters省略 } public static enum TestEnum{ ENUM01("enum_01"),ENUM02("enum_01"),ENUM03("enum_01"); private String title; TestEnum(String title) { this.title = title; } @Override public String toString() { return title; } }
6.SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED
序列化單元素數組時不以數組來輸出,默認false
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING,true);
舉例:
@Test public void singleElemArraysUnwrap() throws Exception { TestPOJO testPOJO = new TestPOJO(); testPOJO.setName("myName"); List<Integer> counts = new ArrayList<>(); counts.add(1); testPOJO.setCounts(counts); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED,false); String jsonStr1 = objectMapper.writeValueAsString(testPOJO); Assert.assertEquals("{\"name\":\"myName\",\"counts\":[1]}",jsonStr1); ObjectMapper objectMapper2 = new ObjectMapper(); objectMapper2.configure(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED,true); String jsonStr2 = objectMapper2.writeValueAsString(testPOJO); Assert.assertEquals("{\"name\":\"myName\",\"counts\":1}",jsonStr2); } public static class TestPOJO{ private String name; private List<Integer> counts; //getters、setters省略 }
7.SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS
序列化Map時對key進行排序操作,默認false
objectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS,true);
舉例:
@Test public void orderMapBykey() throws Exception { TestPOJO testPOJO = new TestPOJO(); testPOJO.setName("myName"); Map<String,Integer> counts = new HashMap<>(); counts.put("a",1); counts.put("d",4); counts.put("c",3); counts.put("b",2); counts.put("e",5); testPOJO.setCounts(counts); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS,false); String jsonStr1 = objectMapper.writeValueAsString(testPOJO); Assert.assertEquals("{\"name\":\"myName\",\"counts\":{\"d\":4,\"e\":5,\"b\":2,\"c\":3,\"a\":1}}",jsonStr1); ObjectMapper objectMapper2 = new ObjectMapper(); objectMapper2.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS,true); String jsonStr2 = objectMapper2.writeValueAsString(testPOJO); Assert.assertEquals("{\"name\":\"myName\",\"counts\":{\"a\":1,\"b\":2,\"c\":3,\"d\":4,\"e\":5}}",jsonStr2); } public static class TestPOJO{ private String name; private Map<String,Integer> counts; //getters、setters省略 }
8.SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS
序列化char[]時以json數組輸出,默認false
objectMapper.configure(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS,true);
舉例:
@Test public void charArraysAsJsonArrays() throws Exception { TestPOJO testPOJO = new TestPOJO(); testPOJO.setName("myName"); char[] counts = new char[]{'a','b','c','d'}; testPOJO.setCounts(counts); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS,false); String jsonStr1 = objectMapper.writeValueAsString(testPOJO); Assert.assertEquals("{\"name\":\"myName\",\"counts\":\"abcd\"}",jsonStr1); ObjectMapper objectMapper2 = new ObjectMapper(); objectMapper2.configure(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS,true); String jsonStr2 = objectMapper2.writeValueAsString(testPOJO); Assert.assertEquals("{\"name\":\"myName\",\"counts\":[\"a\",\"b\",\"c\",\"d\"]}",jsonStr2); } public static class TestPOJO{ private String name; private char[] counts; //getters、setters省略 }
9.SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN
序列化BigDecimal時之間輸出原始數字還是科學計數,默認false,即是否以toPlainString()科學計數方式來輸出
objectMapper.configure(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS,true);
舉例:
@Test public void bigDecimalAsPlain() throws Exception { TestPOJO testPOJO = new TestPOJO(); testPOJO.setName("myName"); testPOJO.setCount(new BigDecimal("1e20")); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN,false); String jsonStr1 = objectMapper.writeValueAsString(testPOJO); Assert.assertEquals("{\"name\":\"myName\",\"count\":1E+20}",jsonStr1); ObjectMapper objectMapper2 = new ObjectMapper(); objectMapper2.configure(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN,true); String jsonStr2 = objectMapper2.writeValueAsString(testPOJO); Assert.assertEquals("{\"name\":\"myName\",\"count\":100000000000000000000}",jsonStr2); }
更多的序列化配置參見點擊打開鏈接,配置DeserializationFeature,反序列化的配置這里不再多做解釋,參見點擊打開鏈接。
需要注意的是對於第二種通過配置SerializationConfig和DeserializationConfig方式只能啟動/禁止自動檢測,無法修改我們所需的可見級別。
有時候對每個實例進行可見級別的注解可能會非常麻煩,這時候我們需要配置一個全局的可見級別,通過objectMapper.setVisibilityChecker()來實現,默認的VisibilityChecker實現類為VisibilityChecker.Std,這樣可以滿足實現復雜場景下的基礎配置。
也有一些實用簡單的可見級別配置,比如:
ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY) // auto-detect all member fields .setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE) // but only public getters .setVisibility(PropertyAccessor.IS_GETTER, JsonAutoDetect.Visibility.NONE) // and none of "is-setters" ;
你也可以通過下面方式來禁止所有的自動檢測功能。
ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibilityChecker(objectMapper.getVisibilityChecker().with(JsonAutoDetect.Visibility.NONE));
三.jackson的常用注解
1.@JsonAutoDetect
看上面自動檢測,不再重復
2.@JsonIgnore
作用在字段或方法上,用來完全忽略被注解的字段和方法對應的屬性,即便這個字段或方法可以被自動檢測到或者還有其他的注解。
舉例:
@Test public void jsonIgnoreTest() throws Exception { TestPOJO testPOJO = new TestPOJO(); testPOJO.setId(111); testPOJO.setName("myName"); testPOJO.setCount(22); ObjectMapper objectMapper = new ObjectMapper(); String jsonStr = objectMapper.writeValueAsString(testPOJO); Assert.assertEquals("{\"id\":111}",jsonStr); String jsonStr2 = "{\"id\":111,\"name\":\"myName\",\"count\":22}"; TestPOJO testPOJO2 = objectMapper.readValue(jsonStr2, TestPOJO.class); Assert.assertEquals(111,testPOJO2.getId()); Assert.assertNull(testPOJO2.getName()); Assert.assertEquals(0,testPOJO2.getCount()); } public static class TestPOJO{ private int id; @JsonIgnore private String name; private int count; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getCount() { return count; } @JsonIgnore public void setCount(int count) { this.count = count; } }
當@JsonIgnore不管注解在getters上還是setters上都會忽略對應的屬性
3.@JsonProperty
作用在字段或方法上,用來對屬性的序列化/反序列化,可以用來避免遺漏屬性,同時提供對屬性名稱重命名,比如在很多場景下Java對象的屬性是按照規范的駝峰書寫,但是實際展示的卻是類似C-style或C++/Microsolft style。
舉例:
@Test public void jsonPropertyTest() throws Exception { TestPOJO testPOJO = new TestPOJO(); testPOJO.wahaha(111); testPOJO.setFirstName("myName"); ObjectMapper objectMapper = new ObjectMapper(); String jsonStr = objectMapper.writeValueAsString(testPOJO); Assert.assertEquals("{\"id\":111,\"first_name\":\"myName\"}",jsonStr); String jsonStr2 = "{\"id\":111,\"first_name\":\"myName\"}"; TestPOJO testPOJO2 = objectMapper.readValue(jsonStr2, TestPOJO.class); Assert.assertEquals(111, testPOJO2.wahaha()); Assert.assertEquals("myName", testPOJO2.getFirstName()); } public static class TestPOJO{ @JsonProperty//注意這里必須得有該注解,因為沒有提供對應的getId和setId函數,而是其他的getter和setter,防止遺漏該屬性 private int id; @JsonProperty("first_name") private String firstName; public int wahaha() { return id; } public void wahaha(int id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } }
4.@JsonIgnoreProperties
作用在類上,用來說明有些屬性在序列化/反序列化時需要忽略掉,可以將它看做是@JsonIgnore的批量操作,但它的功能比@JsonIgnore要強,比如一個類是代理類,我們無法將將@JsonIgnore標記在屬性或方法上,此時便可用@JsonIgnoreProperties標注在類聲明上,它還有一個重要的功能是作用在反序列化時解析字段時過濾一些未知的屬性,否則通常情況下解析到我們定義的類不認識的屬性便會拋出異常。
可以注明是想要忽略的屬性列表如@JsonIgnoreProperties({"name","age","title"}),
也可以注明過濾掉未知的屬性如@JsonIgnoreProperties(ignoreUnknown=true)
舉例:
@Test(expected = UnrecognizedPropertyException.class) public void JsonIgnoreProperties() throws Exception { TestPOJO testPOJO = new TestPOJO(); testPOJO.setId(111); testPOJO.setName("myName"); testPOJO.setAge(22); ObjectMapper objectMapper = new ObjectMapper(); String jsonStr = objectMapper.writeValueAsString(testPOJO); Assert.assertEquals("{\"id\":111}",jsonStr);//name和age被忽略掉了 String jsonStr2 = "{\"id\":111,\"name\":\"myName\",\"age\":22,\"title\":\"myTitle\"}"; TestPOJO testPOJO2 = objectMapper.readValue(jsonStr2, TestPOJO.class); Assert.assertEquals(111, testPOJO2.getId()); Assert.assertNull(testPOJO2.getName()); Assert.assertEquals(0,testPOJO2.getAge()); String jsonStr3 = "{\"id\":111,\"name\":\"myName\",\"count\":33}";//這里有個未知的count屬性,反序列化會報錯 objectMapper.readValue(jsonStr3, TestPOJO.class); } @JsonIgnoreProperties({"name","age","title"}) public static class TestPOJO{ private int id; private String name; private int age; //getters、setters省略 }
如果將上面的
@JsonIgnoreProperties({"name","age","title"})
更換為
@JsonIgnoreProperties(ignoreUnknown=true)
那么測試用例中在反序列化未知的count屬性時便不會拋出異常了。
5.@JsonUnwrapped
作用在屬性字段或方法上,用來將子JSON對象的屬性添加到封閉的JSON對象,說起來比較難懂,看個例子就很清楚了,不多解釋
舉例:
@Test public void jsonUnwrapped() throws Exception { TestPOJO testPOJO = new TestPOJO(); testPOJO.setId(111); TestName testName = new TestName(); testName.setFirstName("張"); testName.setSecondName("三"); testPOJO.setName(testName); ObjectMapper objectMapper = new ObjectMapper(); String jsonStr = objectMapper.writeValueAsString(testPOJO); //如果沒有@JsonUnwrapped,序列化后將為{"id":111,"name":{"firstName":"張","secondName":"三"}} //因為在name屬性上加了@JsonUnwrapped,所以name的子屬性firstName和secondName將不會包含在name中。 Assert.assertEquals("{\"id\":111,\"firstName\":\"張\",\"secondName\":\"三\"}",jsonStr); String jsonStr2 = "{\"id\":111,\"firstName\":\"張\",\"secondName\":\"三\"}"; TestPOJO testPOJO2 = objectMapper.readValue(jsonStr2,TestPOJO.class); Assert.assertEquals(111,testPOJO2.getId()); Assert.assertEquals("張",testPOJO2.getName().getFirstName()); Assert.assertEquals("三",testPOJO2.getName().getSecondName()); } public static class TestPOJO{ private int id; @JsonUnwrapped private TestName name; //getters、setters省略 } public static class TestName{ private String firstName; private String secondName; //getters、setters省略 }
在2.0+版本中@JsonUnwrapped添加了prefix和suffix屬性,用來對字段添加前后綴,這在有關屬性分組上比較有用,在上面的測試用例中,如果我們將TestPOJO的name屬性上的@JsonUnwrapped添加前后綴配置,即
@JsonUnwrapped(prefix = "name_",suffix = "_test")
那么TestPOJO序列化后將為{"id":111,"name_firstName_test":"張","name_secondName_test":"三"},反序列化時也要加上前后綴才會被解析為POJO
6.@JsonIdentityInfo
2.0+版本新注解,作用於類或屬性上,被用來在序列化/反序列化時為該對象或字段添加一個對象識別碼,通常是用來解決循環嵌套的問題,比如數據庫中的多對多關系,通過配置屬性generator來確定識別碼生成的方式,有簡單的,配置屬性property來確定識別碼的名稱,識別碼名稱沒有限制。
對象識別碼可以是虛擬的,即存在在JSON中,但不是POJO的一部分,這種情況下我們可以如此使用注解。
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class,property = "@id")
對象識別碼也可以是真實存在的,即以對象的屬性為識別碼,通常這種情況下我們一般以id屬性為識別碼,可以這么使用注解
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,property = "id")
舉例:
@Test public void jsonIdentityInfo() throws Exception { Parent parent = new Parent(); parent.setName("jack"); Child child = new Child(); child.setName("mike"); Child[] children = new Child[]{child}; parent.setChildren(children); child.setParent(parent); ObjectMapper objectMapper = new ObjectMapper(); String jsonStr = objectMapper.writeValueAsString(parent); Assert.assertEquals("{\"@id\":1,\"name\":\"jack\",\"children\":[{\"name\":\"mike\",\"parent\":1}]}",jsonStr); } @JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class,property = "@id") public static class Parent{ private String name; private Child[] children; //getters、setters省略 } public static class Child{ private String name; private Parent parent; //getters、setters省略 }
這里需要提醒一下的是在1.6版本中提供了@JsonManagedReference和@JsonBackReference來解決循環嵌套問題,因為屬於過時注解這里就不解釋了,有興趣的可以自己看
7.@JsonNaming
jackson 2.1+版本的注解,作用於類或方法,注意這個注解是在jackson-databind包中而不是在jackson-annotations包里,它可以讓你定制屬性命名策略,作用和前面提到的@JsonProperty的重命名屬性名稱相同。比如
你有一個JSON串{"in_reply_to_user_id":"abc123"},需要反序列化為POJO,POJO一般情況下則需要如此寫。
public static class TestPOJO{ private String in_reply_to_user_id; public String getIn_reply_to_user_id() { return in_reply_to_user_id; } public void setIn_reply_to_user_id(String in_reply_to_user_id) { this.in_reply_to_user_id = in_reply_to_user_id; } }
但這顯然不符合JAVA的編碼規范,你可以用@JsonProperty,比如:
public static class TestPOJO{ @JsonProperty("in_reply_to_user_id") private String inReplyToUserId; public String getInReplyToUserId() { return inReplyToUserId; } public void setInReplyToUserId(String inReplyToUserId) { this.inReplyToUserId = inReplyToUserId; } }
這樣就符合規范了,可是如果POJO里有很多屬性,給每個屬性都要加上@JsonProperty是多么繁重的工作,這里就需要用到@JsonNaming了,它不僅能制定統一的命名規則,還能任意按自己想要的方式定制
舉例:
@Test public void jsonNaming() throws Exception{ String jsonStr = "{\"in_reply_to_user_id\":\"abc123\"}"; ObjectMapper objectMapper = new ObjectMapper(); TestPOJO testPOJO = objectMapper.readValue(jsonStr,TestPOJO.class); Assert.assertEquals("abc123",testPOJO.getInReplyToUserId()); TestPOJO testPOJO2 = new TestPOJO(); testPOJO2.setInReplyToUserId("abc123"); String jsonStr2 = objectMapper.writeValueAsString(testPOJO2); Assert.assertEquals(jsonStr,jsonStr2); } @JsonNaming(PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy.class) public static class TestPOJO{ private String inReplyToUserId; public String getInReplyToUserId() { return inReplyToUserId; } public void setInReplyToUserId(String inReplyToUserId) { this.inReplyToUserId = inReplyToUserId; } }
@JsonNaming使用了jackson已經實現的PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy,它可以將大寫轉換為小寫並添加下划線。你可以自定義,必須繼承類PropertyNamingStrategy,建議繼承PropertyNamingStrategyBase,我們自己實現一個類似LowerCaseWithUnderscoresStrategy的策略,只是將下划線改為破折號
舉例:
@Test public void jsonNaming() throws Exception{ String jsonStr = "{\"in-reply-to-user-id\":\"abc123\"}"; ObjectMapper objectMapper = new ObjectMapper(); TestPOJO testPOJO = objectMapper.readValue(jsonStr,TestPOJO.class); Assert.assertEquals("abc123", testPOJO.getInReplyToUserId()); TestPOJO testPOJO2 = new TestPOJO(); testPOJO2.setInReplyToUserId("abc123"); String jsonStr2 = objectMapper.writeValueAsString(testPOJO2); Assert.assertEquals(jsonStr, jsonStr2); } @JsonNaming(MyPropertyNamingStrategy.class) public static class TestPOJO{ private String inReplyToUserId; public String getInReplyToUserId() { return inReplyToUserId; } public void setInReplyToUserId(String inReplyToUserId) { this.inReplyToUserId = inReplyToUserId; } } public static class MyPropertyNamingStrategy extends PropertyNamingStrategy.PropertyNamingStrategyBase { @Override public String translate(String input) { if (input == null) return input; // garbage in, garbage out int length = input.length(); StringBuilder result = new StringBuilder(length * 2); int resultLength = 0; boolean wasPrevTranslated = false; for (int i = 0; i < length; i++) { char c = input.charAt(i); if (i > 0 || c != '-') // skip first starting underscore { if (Character.isUpperCase(c)) { if (!wasPrevTranslated && resultLength > 0 && result.charAt(resultLength - 1) != '-') { result.append('-'); resultLength++; } c = Character.toLowerCase(c); wasPrevTranslated = true; } else { wasPrevTranslated = false; } result.append(c); resultLength++; } } return resultLength > 0 ? result.toString() : input; } }
如果你想讓自己定制的策略對所有解析都實現,除了對每個具體的實體類對應的位置加上@JsonNaming外你還可以如下做全局配置
ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setPropertyNamingStrategy(new MyPropertyNamingStrategy());
四.多態類型處理
jackson允許配置多態類型處理,當進行反序列話時,JSON數據匹配的對象可能有多個子類型,為了正確的讀取對象的類型,我們需要添加一些類型信息。可以通過下面幾個注解來實現:
1.@JsonTypeInfo
作用於類/接口,被用來開啟多態類型處理,對基類/接口和子類/實現類都有效
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,include = JsonTypeInfo.As.PROPERTY,property = "name")
這個注解有一些屬性,use:定義使用哪一種類型識別碼,它有下面幾個可選值:
a.JsonTypeInfo.Id.CLASS:使用完全限定類名做識別
b.JsonTypeInfo.Id.MINIMAL_CLASS:若基類和子類在同一包類,使用類名(忽略包名)作為識別碼
c.JsonTypeInfo.Id.NAME:一個合乎邏輯的指定名稱
d.JsonTypeInfo.Id.CUSTOM:自定義識別碼,由@JsonTypeIdResolver對應,稍后解釋
e.JsonTypeInfo.Id.NONE:不使用識別碼
include(可選):指定識別碼是如何被包含進去的,它有下面幾個可選值:
a.JsonTypeInfo.As.PROPERTY:作為數據的兄弟屬性
b.JsonTypeInfo.As.EXISTING_PROPERTY:作為POJO中已經存在的屬性
c.JsonTypeInfo.As.EXTERNAL_PROPERTY:作為擴展屬性
d.JsonTypeInfo.As.WRAPPER_OBJECT:作為一個包裝的對象
e.JsonTypeInfo.As.WRAPPER_ARRAY:作為一個包裝的數組
property(可選):制定識別碼的屬性名稱
此屬性只有當use為JsonTypeInfo.Id.CLASS(若不指定property則默認為@class).JsonTypeInfo.Id.MINIMAL_CLASS(若不指定property則默認為@c).JsonTypeInfo.Id.NAME(若不指定property默認為@type),include為JsonTypeInfo.As.PROPERTY.JsonTypeInfo.As.EXISTING_PROPERTY.JsonTypeInfo.As.EXTERNAL_PROPERTY時才有效
defaultImpl(可選):如果類型識別碼不存在或者無效,可以使用該屬性來制定反序列化時使用的默認類型
visible(可選,默認為false):是否可見屬性定義了類型標識符的值是否會通過JSON流成為反序列化器的一部分,默認為fale,也就是說,jackson會從JSON內容中處理和刪除類型標識符再傳遞給JsonDeserializer。
2.@JsonSubTypes
作用於類/接口,用來列出給定類的子類,只有當子類類型無法被檢測到時才會使用它。一般是配合@JsonTypeInfo在基類上使用,比如:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,include = JsonTypeInfo.As.PROPERTY,property = "typeName") @JsonSubTypes({@JsonSubTypes.Type(value=Sub1.class,name = "sub1"),@JsonSubTypes.Type(value=Sub2.class,name = "sub2")})
@JsonSubTypes的值是一個@JsonSubTypes.Type[]數組,里面枚舉了多態類型(value對應類)和類型的標識符值(name對應@JsonTypeInfo中的property標識名稱的值,此為可選值,若不制定需由@JsonTypeName在子類上制定)
3.@JsonTypeName
作用於子類,用來為多態子類指定類型標識符的值
比如:
@JsonTypeName(value = "sub1")
value屬性作用同上面@JsonSubTypes里的name作用
4.@JsonTypeResolver和@JsonTypeIdResoler
作用於類,可以自定義多態的類型標識符,這個平時很少用到,主要是現有的一般就已經滿足絕大多數的需求了,如果你需要比較特別的類型標識符,建議使用這2個注解,自己定制基於TypeResolverBuilder和TypeIdResolver的類即可。
我們看幾個jackson處理多態的例子:
@Test public void jsonTypeInfo() throws Exception{ Sub1 sub1 = new Sub1(); sub1.setId(1); sub1.setName("sub1Name"); Sub2 sub2 = new Sub2(); sub2.setId(2); sub2.setAge(33); ObjectMapper objectMapper = new ObjectMapper(); TestPOJO testPOJO = new TestPOJO(); testPOJO.setMyIns(new MyIn[]{sub1, sub2}); String jsonStr = objectMapper.writeValueAsString(testPOJO); Assert.assertEquals("{\"myIns\":[{\"id\":1,\"name\":\"sub1Name\"},{\"id\":2,\"age\":33}]}", jsonStr); System.out.println(jsonStr); } public static abstract class MyIn{ private int id; //getters、setters省略 } public static class Sub1 extends MyIn{ private String name; //getters、setters省略 } public static class Sub2 extends MyIn{ private int age; //getters、setters省略 }
這是序列化時最簡單的一種多態處理方式,因為沒有使用任何多態處理注解,即默認使用的識別碼類型為JsonTypeInfo.Id.NONE,而jackson沒有自動搜索功能,所以只能序列化而不能反序列化,上面序列化測試的結果為{"myIns":[{"id":1,"name":"sub1Name"},{"id":2,"age":33}]},我們可以看到JSON串中是沒有對應的多態類型識別碼的。
下面我們在基類MyIn上加上多態處理相關注解,首先我們在基類MyIn上添加@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)即
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) public static abstract class MyIn{ private int id; //getters、setters省略 }
執行上面的序列化測試代碼結果將會是:{"myIns":[{"@class":"cn.yangyong.fodder.util.JacksonUtilsTest$Sub1","id":1,"name":"sub1Name"},{"@class":"cn.yangyong.fodder.util.JacksonUtilsTest$Sub2","id":2,"age":33}]}
我們可以看到多了相應的多態類型識別碼,識別碼名稱為默認的@class(因為沒有指定名稱),識別碼的值為JsonTypeInfo.Id.CLASS即子類完全限定名。
我們再添加上property屬性@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS,property = "typeName")即
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS,property = "typeName") public static abstract class MyIn{ private int id; //getters、setters省略 }
再次執行上面的序列化測試代碼結果將會是:{"myIns":[{"typeName":"cn.yangyong.fodder.util.JacksonUtilsTest$Sub1","id":1,"name":"sub1Name"},{"typeName":"cn.yangyong.fodder.util.JacksonUtilsTest$Sub2","id":2,"age":33}]}
這次多態類型識別碼的名稱已經變成了我們指定的typeName而不是默認的@class了
上面的例子都是默認選擇的include為JsonTypeInfo.As.PROPERTY,下面我們更改include方式,看看有什么變化,將include設置為JsonTypeInfo.As.WRAPPER_OBJECT即
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS,include = JsonTypeInfo.As.WRAPPER_OBJECT,property = "typeName") public static abstract class MyIn{ private int id; //getters、setters省略 }
再次執行序列化測試,結果為:{"myIns":[{"cn.yangyong.fodder.util.JacksonUtilsTest$Sub1":{"id":1,"name":"sub1Name"}},{"cn.yangyong.fodder.util.JacksonUtilsTest$Sub2":{"id":2,"age":33}}]}
我們看到類型識別碼不再成為兄弟屬性包含進去了而是為父屬性將其他屬性包含進去,此時我們指定的property=“typeName”已經無用了
再次修改use屬性指定為JsonTypeInfo.Id.MINIMAL_CLASS,即@JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS,include = JsonTypeInfo.As.PROPERTY,property = "typeName")
@JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS,include = JsonTypeInfo.As.PROPERTY,property = "typeName") public static abstract class MyIn{ private int id; //getters、setters省略 }
測試序列化結果為:{"myIns":[{"typeName":".JacksonUtilsTest$Sub1","id":1,"name":"sub1Name"},{"typeName":".JacksonUtilsTest$Sub2","id":2,"age":33}]}
發現已經沒有同包的package名稱,識別碼的值更加簡短了
測試反序列化:
@Test public void jsonTypeInfo() throws Exception{ ObjectMapper objectMapper = new ObjectMapper(); String jsonStr2 = "{\"myIns\":[{\"typeName\":\".JacksonUtilsTest$Sub1\",\"id\":1,\"name\":\"sub1Name\"},{\"typeName\":\".JacksonUtilsTest$Sub2\",\"id\":2,\"age\":33}]}"; TestPOJO testPOJO2 = objectMapper.readValue(jsonStr2,TestPOJO.class); MyIn[] myIns = testPOJO2.getMyIns(); for (MyIn myIn : myIns) { System.out.println(myIn.getClass().getSimpleName()); } }
結果將會顯示為Sub1和Sub2說明是可以實現多態的反序列化的。可能我們在反序列化時覺得如此傳遞識別碼很不友好,最好可以自定義識別碼的值,可以選擇use = JsonTypeInfo.Id.NAME和@JsonSubTypes配合即
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,include = JsonTypeInfo.As.PROPERTY,property = "typeName") @JsonSubTypes({@JsonSubTypes.Type(value=Sub1.class,name="sub1"),@JsonSubTypes.Type(value=Sub2.class,name="sub2")}) public static abstract class MyIn{ private int id; //getters、setters省略 }
執行序列化結果為:{"myIns":[{"typeName":"sub1","id":1,"name":"sub1Name"},{"typeName":"sub2","id":2,"age":33}]}
使用這個結果反序列化也可以得到我們想要的結果,或者在子類上添加@JsonTypeName(value = "sub1")和@JsonTypeName(value = "sub2")以便取代@JsonSubTypes里的name
如果想不使用@JsonSubTypes來實現反序列化,我們可以在ObjectMapper上注冊子類實現,即
ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerSubtypes(new NamedType(Sub1.class,"sub1")); objectMapper.registerSubtypes(new NamedType(Sub2.class,"sub2"));
更多多態處理的例子還請大家自己研究。
五.用於序列化和反序列化的注解類
1.@JsonSerialize和@JsonDeserialize
作用於方法和字段上,通過 using(JsonSerializer)和using(JsonDeserializer)來指定序列化和反序列化的實現,通常我們在需要自定義序列化和反序列化時會用到,比如下面的例子中的日期轉換
@Test public void jsonSerializeAndDeSerialize() throws Exception { TestPOJO testPOJO = new TestPOJO(); testPOJO.setName("myName"); testPOJO.setBirthday(new Date()); ObjectMapper objectMapper = new ObjectMapper(); String jsonStr = objectMapper.writeValueAsString(testPOJO); System.out.println(jsonStr); String jsonStr2 = "{\"name\":\"myName\",\"birthday\":\"2014-11-11 19:01:58\"}"; TestPOJO testPOJO2 = objectMapper.readValue(jsonStr2,TestPOJO.class); System.out.println(testPOJO2.toString()); } public static class TestPOJO{ private String name; @JsonSerialize(using = MyDateSerializer.class) @JsonDeserialize(using = MyDateDeserializer.class) private Date birthday; //getters、setters省略 @Override public String toString() { return "TestPOJO{" + "name='" + name + '\'' + ", birthday=" + birthday + '}'; } } private static class MyDateSerializer extends JsonSerializer<Date>{ @Override public void serialize(Date value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException { DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateStr = dateFormat.format(value); jgen.writeString(dateStr); } } private static class MyDateDeserializer extends JsonDeserializer<Date>{ @Override public Date deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { String value = jp.getValueAsString(); DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); try { return dateFormat.parse(value); } catch (ParseException e) { e.printStackTrace(); } return null; } }
上面的例子中自定義了日期的序列化和反序列化方式,可以將Date和指定日期格式字符串之間相互轉換。
也可以通過使用as(JsonSerializer)和as(JsonDeserializer)來實現多態類型轉換,上面我們有提到多態類型處理時可以使用@JsonTypeInfo實現,還有一種比較簡便的方式就是使用@JsonSerialize和@JsonDeserialize指定as的子類類型,注意這里必須指定為子類類型才可以實現替換運行時的類型。
@Test public void jsonSerializeAndDeSerialize() throws Exception { TestPOJO testPOJO = new TestPOJO(); testPOJO.setName("myName"); Sub1 sub1 = new Sub1(); sub1.setId(1); sub1.setName("sub1Name"); Sub2 sub2 = new Sub2(); sub2.setId(2); sub2.setAge(22); testPOJO.setSub1(sub1); testPOJO.setSub2(sub2); ObjectMapper objectMapper = new ObjectMapper(); String jsonStr = objectMapper.writeValueAsString(testPOJO); System.out.println(jsonStr); String jsonStr2 = "{\"name\":\"myName\",\"sub1\":{\"id\":1,\"name\":\"sub1Name\"},\"sub2\":{\"id\":2,\"age\":22}}"; TestPOJO testPOJO2 = objectMapper.readValue(jsonStr2,TestPOJO.class); System.out.println(testPOJO2.toString()); } public static class TestPOJO{ private String name; @JsonSerialize(as = Sub1.class) @JsonDeserialize(as = Sub1.class) private MyIn sub1; @JsonSerialize(as = Sub2.class) @JsonDeserialize(as = Sub2.class) private MyIn sub2; //getters、setters省略 @Override public String toString() { return "TestPOJO{" + "name='" + name + '\'' + ", sub1=" + sub1 + ", sub2=" + sub2 + '}'; } } public static class MyIn{ private int id; //getters、setters省略 } public static class Sub1 extends MyIn{ private String name; //getters、setters省略 @Override public String toString() { return "Sub1{" + "id=" + getId() + "name='" + name + '\'' + '}'; } } public static class Sub2 extends MyIn{ private int age; //getters、setters省略 @Override public String toString() { return "Sub1{" + "id=" + getId() + "age='" + age + '}'; } }
上面例子中通過as來指定了需要替換實際運行時類型的子類,實際上上面例子中序列化時是可以不使用@JsonSerialize(as = Sub1.class)的,因為jackson可以自動將POJO轉換為對應的JSON,而反序列化時由於無法自動檢索匹配類型必須要指定@JsonDeserialize(as = Sub1.class)方可實現。
最后@JsonSerialize可以配置include屬性來指定序列化時被注解的屬性被包含的方式,默認總是被包含進來,但是可以過濾掉空的屬性或有默認值的屬性,舉個簡單的過濾空屬性的例子如下:
@Test public void jsonSerializeAndDeSerialize() throws Exception { TestPOJO testPOJO = new TestPOJO(); testPOJO.setName(""); ObjectMapper objectMapper = new ObjectMapper(); String jsonStr = objectMapper.writeValueAsString(testPOJO); Assert.assertEquals("{}",jsonStr); } public static class TestPOJO{ @JsonSerialize(include = JsonSerialize.Inclusion.NON_EMPTY) private String name; //getters、setters省略 }
2.@JsonPropertyOrder
作用在類上,被用來指明當序列化時需要對屬性做排序,它有2個屬性。
一個是alphabetic:布爾類型,表示是否采用字母拼音順序排序,默認是為false,即不排序
@Test public void jsonPropertyOrder() throws Exception { TestPOJO testPOJO = new TestPOJO(); testPOJO.setA("1"); testPOJO.setB("2"); testPOJO.setC("3"); testPOJO.setD("4"); ObjectMapper objectMapper = new ObjectMapper(); String jsonStr = objectMapper.writeValueAsString(testPOJO); Assert.assertEquals("{\"a\":\"1\",\"c\":\"3\",\"d\":\"4\",\"b\":\"2\"}",jsonStr); } public static class TestPOJO{ private String a; private String c; private String d; private String b; //getters、setters省略 }
我們先看一個默認的排序方式,序列化單元測試結果依次為{"a":"1","c":"3","d":"4","b":"2"},即是沒有經過排序操作的,在TestPOJO上加上@jsonPropertyOrder(alphabetic = true)再執行測試結果將會為{"a":"1","b":"2","c":"3","d":"4"}
還有一個屬性是value:數組類型,表示將優先其他屬性排序的屬性名稱
@Test public void jsonPropertyOrder() throws Exception { TestPOJO testPOJO = new TestPOJO(); testPOJO.setA("1"); testPOJO.setB("2"); testPOJO.setC("3"); testPOJO.setD("4"); ObjectMapper objectMapper = new ObjectMapper(); String jsonStr = objectMapper.writeValueAsString(testPOJO); System.out.println(jsonStr); Assert.assertEquals("{\"c\":\"3\",\"b\":\"2\",\"a\":\"1\",\"d\":\"4\"}",jsonStr); } @JsonPropertyOrder(alphabetic = true,value = {"c","b"}) public static class TestPOJO{ private String a; private String c; private String d; private String b; //getters、setters省略 }
上面例子可以看到value指定了c和b屬性優先排序,所以序列化后為{"c":"3","b":"2","a":"1","d":"4"}
還記得本文上面最開始配置MapperFeature時也有屬性排序么,對,就是。
objectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY,true);
只不過@JsonPropertyOrder顆粒度要更細一點,可以決定哪些屬性優先排序
3.@JsonView
視圖模板,作用於方法和屬性上,用來指定哪些屬性可以被包含在JSON視圖中,在前面我們知道已經有@JsonIgnore和@JsonIgnoreProperties可以排除過濾掉不需要序列化的屬性,可是如果一個POJO中有上百個屬性,比如訂單類、商品詳情類這種屬性超多,而我們可能只需要概要簡單信息即序列化時只想輸出其中幾個或10幾個屬性,此時使用@JsonIgnore和@JsonIgnoreProperties就顯得非常繁瑣,而使用@JsonView便會非常方便,只許在你想要輸出的屬性(或對應的getter)上添加@JsonView即可,舉例:
@Test public void jsonView() throws Exception { TestPOJO testPOJO = new TestPOJO(); testPOJO.setA("1"); testPOJO.setB("2"); testPOJO.setC("3"); testPOJO.setD("4"); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false); String jsonStr = objectMapper.writerWithView(FilterView.OutputA.class).writeValueAsString(testPOJO); Assert.assertEquals("{\"a\":\"1\",\"c\":\"3\"}",jsonStr); String jsonStr2 = objectMapper.writerWithView(FilterView.OutputB.class).writeValueAsString(testPOJO); Assert.assertEquals("{\"d\":\"4\",\"b\":\"2\"}",jsonStr2); } public static class TestPOJO{ @JsonView(FilterView.OutputA.class) private String a; @JsonView(FilterView.OutputA.class) private String c; @JsonView(FilterView.OutputB.class) private String d; @JsonView(FilterView.OutputB.class) private String b; //getters、setters忽略 } private static class FilterView { static class OutputA {} static class OutputB {} }
上面的測試用例中,我們在序列化之前先設置了objectMapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false),看javadoc說這是一個雙向開關,開啟將輸出沒有JsonView注解的屬性,false關閉將輸出有JsonView注解的屬性,可惜我在測試中開啟開關后有JsonView注解的屬性任然輸出了,大家可以研究下。序列化時使用了objectMapper.writerWithView(FilterView.OutputA.class).writeValueAsString(testPOJO),即使用哪個視圖來輸出。在上面的例子中又2種視圖,我們在序列化的時候可以選擇想要的視圖來輸出,這在一些地方比較好用,比如安卓、蘋果、桌面等不同的客戶端可能會輸出不同的屬性。在1.6版本中這個@JsonView注解同時也會強制性自動發現,也就是說不管屬性的可見性以及是否設置了自動發現這些屬性都將會自動被發現,在上例中TestPOJO中的getters、setters可以不需要也能輸出我們想要的結果。
4.@JsonFilter
Json屬性過濾器,作用於類,作用同上面的@JsonView,都是過濾掉不想要的屬性,輸出自己想要的屬性。和@FilterView不同的是@JsonFilter可以動態的過濾屬性,比如我不想輸出以system開頭的所有屬性等待,應該說@JsonFilter更高級一點,舉個簡單的例子。
@Test public void jsonFilter() throws Exception { TestPOJO testPOJO = new TestPOJO(); testPOJO.setA("1"); testPOJO.setB("2"); testPOJO.setC("3"); testPOJO.setD("4"); ObjectMapper objectMapper = new ObjectMapper(); FilterProvider filters = new SimpleFilterProvider().addFilter("myFilter",SimpleBeanPropertyFilter.filterOutAllExcept("a")); objectMapper.setFilters(filters); String jsonStr = objectMapper.writeValueAsString(testPOJO); Assert.assertEquals("{\"a\":\"1\"}",jsonStr); } @JsonFilter("myFilter") public static class TestPOJO{ private String a; private String c; private String d; private String b; //getters、setters省略 }
上面例子中在我們想要序列化的POJO上加上了@JsonFilter,表示該類將使用名為myFilter的過濾器。在測試中定義了一個名為myFilter的SimpleFilterProvider,這個過濾器將會過濾掉所有除a屬性以外的屬性。這只是最簡單的輸出指定元素的例子,你可以自己實現FilterProvider來滿足你的過濾需求。
有時候我們可能需要根據現有的POJO來過濾屬性,而這種情況下通常不會讓你修改已有的代碼在POJO上加注解,這種情況下我們就可以結合@JsonFilter和MixInAnnotations來實現過濾屬性,如下例所示,不再多做解釋。
@Test public void jsonFilter() throws Exception { TestPOJO testPOJO = new TestPOJO(); testPOJO.setA("1"); testPOJO.setB("2"); testPOJO.setC("3"); testPOJO.setD("4"); ObjectMapper objectMapper = new ObjectMapper(); FilterProvider filters = new SimpleFilterProvider().addFilter("myFilter",SimpleBeanPropertyFilter.filterOutAllExcept("a")); objectMapper.setFilters(filters); objectMapper.addMixInAnnotations(TestPOJO.class,MyFilterMixIn.class); String jsonStr = objectMapper.writeValueAsString(testPOJO); Assert.assertEquals("{\"a\":\"1\"}",jsonStr); } public static class TestPOJO{ private String a; private String c; private String d; private String b; //getters、setters省略 } @JsonFilter("myFilter") private static interface MyFilterMixIn{ }
5.@JsonIgnoreType
作用於類,表示被注解該類型的屬性將不會被序列化和反序列化,也跟上面幾個一樣屬於過濾屬性功能的注解,舉例:
@Test public void jsonFilter() throws Exception { TestPOJO testPOJO = new TestPOJO(); testPOJO.setName("myName"); Sub1 sub1 = new Sub1(); sub1.setId(1); sub1.setName("sub1"); Sub2 sub2 = new Sub2(); sub2.setId(2); sub2.setAge(22); testPOJO.setMyIn(sub1); testPOJO.setSub1(sub1); testPOJO.setSub2(sub2); ObjectMapper objectMapper = new ObjectMapper(); String jsonStr = objectMapper.writeValueAsString(testPOJO); System.out.println(jsonStr); } public static class TestPOJO{ private Sub1 sub1; private Sub2 sub2; private MyIn myIn; private String name; //getters、setters省略 } public static class MyIn{ private int id; //getters、setters省略 } @JsonIgnoreType public static class Sub1 extends MyIn{ private String name; //getters、setters省略 } @JsonIgnoreType public static class Sub2 extends MyIn{ private int age; //getters、setters省略 }
上面例子中我們在類Sub1和Sub2上都加上了@JsonIgnoreType,那么需要序列化和反序列時POJO中所有Sub1和Sub2類型的屬性都將會被忽略,上面測試結果為{"myIn":{"id":1,"name":"sub1"},"name":"myName"},只輸出了name和myIn屬性。需要注意的是@JsonIgnoreType是可以繼承的,即如果在基類上添加了該注解,那么子類也相當於加了該注解。在上例中,如果只在基類MyIn上添加@JsonIgnoreType那么序列化TestPOJO時將會過濾掉MyIn、Sub1、Sub2。輸出結果為{"name":"myName"}
6.@JsonAnySetter
作用於方法,在反序列化時用來處理遇到未知的屬性的時候調用,在本文前面我們知道可以通過注解@JsonIgnoreProperties(ignoreUnknown=true)來過濾未知的屬性,但是如果需要這些未知的屬性該如何是好?那么@JsonAnySetter就可以派上用場了,它通常會和map屬性配合使用用來保存未知的屬性,舉例:
@Test public void jsonAnySetter() throws Exception { ObjectMapper objectMapper = new ObjectMapper(); String jsonStr = "{\"name\":\"myName\",\"code\":\"12345\",\"age\":12}"; TestPOJO testPOJO = objectMapper.readValue(jsonStr,TestPOJO.class); Assert.assertEquals("myName",testPOJO.getName()); Assert.assertEquals("12345",testPOJO.getOther().get("code")); Assert.assertEquals(12,testPOJO.getOther().get("age")); } public static class TestPOJO{ private String name; private Map other = new HashMap(); @JsonAnySetter public void set(String name,Object value) { other.put(name,value); } //getters、setters省略 }
測試用例中我們在set方法上標注了@JsonAnySetter,每當遇到未知的屬性時都會調用該方法
7.@JsonCreator
作用於方法,通常用來標注構造方法或靜態工廠方法上,使用該方法來構建實例,默認的是使用無參的構造方法,通常是和@JsonProperty或@JacksonInject配合使用,舉例
@Test public void jsonCreator() throws Exception { ObjectMapper objectMapper = new ObjectMapper(); String jsonStr = "{\"full_name\":\"myName\",\"age\":12}"; TestPOJO testPOJO = objectMapper.readValue(jsonStr,TestPOJO.class); Assert.assertEquals("myName",testPOJO.getName()); Assert.assertEquals(12, testPOJO.getAge()); } public static class TestPOJO{ private String name; private int age; @JsonCreator public TestPOJO(@JsonProperty("full_name") String name,@JsonProperty("age") int age){ this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } }
上面示例中是在構造方法上標注了@JsonCreator,同樣你也可以標注在靜態工廠方法上,比如:
@Test public void jsonCreator() throws Exception { ObjectMapper objectMapper = new ObjectMapper(); String jsonStr = "{\"name\":\"myName\",\"birthday\":1416299461556}"; TestPOJO testPOJO = objectMapper.readValue(jsonStr,TestPOJO.class); Assert.assertEquals("myName",testPOJO.getName()); System.out.println(testPOJO.getBirthday()); } public static class TestPOJO{ private String name; private Date birthday; private TestPOJO(String name,Date birthday){ this.name = name; this.birthday = birthday; } @JsonCreator public static TestPOJO getInstance(@JsonProperty("name") String name,@JsonProperty("birthday") long timestamp){ Date date = new Date(timestamp); return new TestPOJO(name,date); } public String getName() { return name; } public Date getBirthday() { return birthday; } }
這個實例中,TestPOJO的構造方法是私有的,外面無法new出來該對象,只能通過工廠方法getInstance來構造實例,此時@JsonCreator就標注在工廠方法上。
除了這2種方式外,還有一種構造方式成為授權式構造器,也是我們平常比較常用到的,這個構造器只有一個參數,且不能使用@JsonProperty。舉例:
@Test public void jsonCreator() throws Exception { ObjectMapper objectMapper = new ObjectMapper(); String jsonStr = "{\"full_name\":\"myName\",\"age\":12}"; TestPOJO testPOJO = objectMapper.readValue(jsonStr,TestPOJO.class); Assert.assertEquals("myName",testPOJO.getName()); Assert.assertEquals(12,testPOJO.getAge()); } public static class TestPOJO{ private String name; private int age; @JsonCreator public TestPOJO(Map map){ this.name = (String)map.get("full_name"); this.age = (Integer)map.get("age"); } public String getName() { return name; } public int getAge() { return age; } }
8.@JacksonInject
作用於屬性、方法、構造參數上,被用來反序列化時標記已經被注入的屬性,舉例:
@Test public void jacksonInject() throws Exception { ObjectMapper objectMapper = new ObjectMapper(); String jsonStr = "{\"age\":12}"; InjectableValues inject = new InjectableValues.Std().addValue("name","myName"); TestPOJO testPOJO = objectMapper.reader(TestPOJO.class).with(inject).readValue(jsonStr); Assert.assertEquals("myName", testPOJO.getName()); Assert.assertEquals(12,testPOJO.getAge()); } public static class TestPOJO{ @JacksonInject("name") private String name; private int age; //getters、setters省略 }
上面例子中我們在反序列化前通過InjectableValues來進行注入我們想要的屬性
9.@JsonPOJOBuilder
作用於類,用來標注如何定制構建對象,使用的是builder模式來構建,比如Value v = new ValueBuilder().withX(3).withY(4).build();這種就是builder模式來構建對象,通常會喝@JsonDeserialize.builder來配合使用,我們舉個例子:
@Test public void jacksonInject() throws Exception { ObjectMapper objectMapper = new ObjectMapper(); String jsonStr = "{\"name\":\"myName\",\"age\":12}"; TestPOJO testPOJO = objectMapper.readValue(jsonStr,TestPOJO.class); Assert.assertEquals("myName", testPOJO.getName()); Assert.assertEquals(12,testPOJO.getAge()); } @JsonDeserialize(builder=TestPOJOBuilder.class) public static class TestPOJO{ private String name; private int age; public TestPOJO(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } } @JsonPOJOBuilder(buildMethodName = "create",withPrefix = "with") public static class TestPOJOBuilder{ private String name; private int age; public TestPOJOBuilder withName(String name) { this.name = name; return this; } public TestPOJOBuilder withAge(int age) { this.age = age; return this; } public TestPOJO create() { return new TestPOJO(name,age); } }
在TestPOJOBuilder上有@JsonPOJOBuilder注解,表示所有的參數傳遞方法都是以with開頭,最終構建好的對象是通過create方法來獲得,而在TestPOJO上使用了@JsonDeserializer,告訴我們在反序列化的時候我們使用的是TestPOJOBuilder來構建此對象的。
還有一些過期不推薦使用的注解,我們一筆帶過,主要知道他們是跟哪些其他注解功能一樣即可
@JsonGetter
作用於方法,1.0版本開始的注解,已經過期,不推薦使用,改用@JsonProperty
@JsonUseSerializer
作用於類和方法,1.5版本開始被移除了,改用@JsonSerialize
@JsonSetter
作用於方法,1.0版本開始的注解,已過期,不推薦使用,改用@JsonProperty
@JsonClass
作用於方法和類,1.9版本開始被移除了,改為@JsonDeserialize.as
@JsonContentClass
作用於方法,1.9版本開始被移除了,改為@JsonDeserialize.contentAs
@JsonKeyClass
作用於方法和類,1.9版本開始被移除了,改為@JsonDeserialize.keyAs
@JsonUseDeserializer
作用於方法和類,1.5版本開始被移除了,改為@JsonDeserialize