Jackson 框架的高階應(yīng)用

轉(zhuǎn)載自:https://www.ibm.com/developerworks/cn/java/jackson-advanced-application/index.html

  • 一、概覽
  • 二、Jackson 的基本用法
      1. ObjectMapper 的使用
      1. 信息配置
      1. Jackson 的 注解的使用
  • 三、Jackson 的高階應(yīng)用
      1. 格式處理(含日期格式)
      1. 泛型反序列化
      1. 屬性可視化
      1. 屬性過濾
      1. 自定義序列化和反序列化
      1. 樹模型處理
  • 四、總結(jié)

一、概覽

Jackson 是當(dāng)前用的比較廣泛的,用來序列化和反序列化 json 的 Java 的開源框架。Jackson 社 區(qū)相對比較活躍,更新速度也比較快, 從 Github 中的統(tǒng)計來看,Jackson 是最流行的 json 解析器之一 。Spring MVC 的默認 json 解析器便是 Jackson。

Jackson 優(yōu)點很多。 Jackson 所依賴的 jar 包較少 ,簡單易用。與其他 Java 的 json 的框架 Gson 等相比, Jackson 解析大的 json 文件速度比較快;Jackson 運行時占用內(nèi)存比較低,性能比較好;Jackson 有靈活的 API,可以很容易進行擴展和定制。

Jackson 的 1.x 版本的包名是 org.codehaus.jackson ,當(dāng)升級到 2.x 版本時,包名變?yōu)?com.fasterxml.jackson,本文討論的內(nèi)容是基于最新的 Jackson 的 2.9.1 版本。

Jackson 的核心模塊由三部分組成。

  • jackson-core,核心包,提供基于"流模式"解析的相關(guān) API,它包括 JsonPaser 和 JsonGenerator。 Jackson 內(nèi)部實現(xiàn)正是通過高性能的流模式 API 的 JsonGenerator 和 JsonParser 來生成和解析 json。
  • jackson-annotations,注解包,提供標(biāo)準(zhǔn)注解功能;
  • jackson-databind ,數(shù)據(jù)綁定包, 提供基于"對象綁定" 解析的相關(guān) API ( ObjectMapper ) 和"樹模型" 解析的相關(guān) API (JsonNode);基于"對象綁定" 解析的 API 和"樹模型"解析的 API 依賴基于"流模式"解析的 API。

在了解 Jackson 的概要情況之后,下面介紹 Jackson 的基本用法。

二、Jackson 的基本用法

若想在 Java 代碼中使用 Jackson 的核心模塊的 jar 包 ,需要在 pom.xml 中添加如下信息。

清單 1.在 pom.xml 的 Jackson 的配置信息

<dependency> 
    <groupId>com.fasterxml.jackson.core</groupId> 
    <artifactId>jackson-databind</artifactId> 
    <version>2.9.1</version> 
</dependency>

jackson-databind 依賴 jackson-corejackson-annotations,當(dāng)添加 jackson-databind 之后, jackson-core 和 jackson-annotations 也隨之添加到 Java 項目工程中。在添加相關(guān)依賴包之后,就可以使用 Jackson。

1. ObjectMapper 的 使用

Jackson 最常用的 API 就是基于"對象綁定" 的 ObjectMapper。下面是一個 ObjectMapper 的使用的簡單示例。

清單 2 . ObjectMapper 使用示例

ObjectMapper mapper = new ObjectMapper(); 
Person person = new Person(); 
person.setName("Tom"); 
person.setAge(40); 
String jsonString = mapper.writerWithDefaultPrettyPrinter() 
                            .writeValueAsString(person); 
Person deserializedPerson = mapper.readValue(jsonString, Person.class);

ObjectMapper 通過 writeValue 系列方法 將 java 對 象序列化 為 json,并 將 json 存 儲成不同的格式,String(writeValueAsString),Byte Array(writeValueAsString),Writer, File,OutStream 和 DataOutput。

ObjectMapper 通過 readValue 系列方法從不同的數(shù)據(jù)源像 String , Byte Array, Reader,F(xiàn)ile,URL, InputStream 將 json 反序列化為 java 對象。

2. 信息配置

在調(diào)用 writeValue 或調(diào)用 readValue 方法之前,往往需要設(shè)置 ObjectMapper 的相關(guān)配置信息。這些配置信息應(yīng)用 java 對象的所有屬性上。示例如下:

清單 3 . 配置信息使用示例

//在反序列化時忽略在 json 中存在但 Java 對象不存在的屬性 
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
   false); 
//在序列化時日期格式默認為 yyyy-MM-dd'T'HH:mm:ss.SSSZ 
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false) 
//在序列化時忽略值為 null 的屬性 
mapper.setSerializationInclusion(Include.NON_NULL); 
//忽略值為默認值的屬性 
mapper.setDefaultPropertyInclusion(Include.NON_DEFAULT);

更多配置信息可以查看 Jackson 的 DeserializationFeature,SerializationFeature 和 I nclude。

3. Jackson 的 注解的使用

Jackson 根據(jù)它的默認方式序列化和反序列化 java 對象,若根據(jù)實際需要,靈活的調(diào)整它的默認方式,可以使用 Jackson 的注解。常用的注解及用法如下。

表 1. Jackson 的 常用注解

注解 用法
@JsonProperty 用于屬性,把屬性的名稱序列化時轉(zhuǎn)換為另外一個名稱。示例:@JsonProperty("birth_ d ate") private Date birthDate;
@JsonFormat 用于屬性或者方法,把屬性的格式序列化時轉(zhuǎn)換成指定的格式。示例:@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm") public Date getBirthDate()
@JsonPropertyOrder 用于類, 指定屬性在序列化時 json 中的順序 , 示例:@JsonPropertyOrder({ "birth_Date", "name" }) public class Person
@JsonCreator 用于構(gòu)造方法,和 @JsonProperty 配合使用,適用有參數(shù)的構(gòu)造方法。 示例:@JsonCreator public Person(@JsonProperty("name")String name) {…}
@JsonAnySetter 用于屬性或者方法,設(shè)置未反序列化的屬性名和值作為鍵值存儲到 map 中@JsonAnySetter public void set(String key, Object value) { map.put(key, value); }
@JsonAnyGetter 用于方法 ,獲取所有未序列化的屬性 public Map<String, Object> any() { return map; }

在了解 Jackson 的基本用法后,下面詳細地介紹它的一些高階應(yīng)用。

三、Jackson 的高階應(yīng)用

1. 格式處理(含日期格式)

不同類型的日期類型,Jackson 的處理方式也不同。

對于日期類型為 + java.util.Calendar,java.util.GregorianCalendar,java.sql.Date,java.util.Date,java.sql.Timestamp,若不指定格式, 在 json 文件中將序列化 為 long 類型的數(shù)據(jù)。顯然這種默認格式,可讀性差,轉(zhuǎn)換格式是必要的。Jackson 有 很多方式轉(zhuǎn)換日期格式。
注解方式,請參照" Jackson 的注解的使用"的@ JsonFormat 的示例。

  • ObjectMapper 方式,調(diào)用 ObjectMapper 的方法 setDateFormat,將序列化為指定格式的 string 類型的數(shù)據(jù)。
  • 對于日期類型為 java.time.LocalDate,還需要添加代碼 mapper.registerModule(new JavaTimeModule()),同時添加相應(yīng)的依賴 jar 包

清單 4 . JSR31 0 的配置信息

<dependency> 
    <groupId>com.fasterxml.jackson.datatype</groupId> 
    <artifactId>jackson-datatype-jsr310</artifactId> 
    <version>2.9.1</version> 
</dependency>

對于 Jackson 2.5 以下版本,需要添加代碼 objectMapper.registerModule(new JSR310Module ())

  • 對于日期類型為 org.joda.time.DateTime,還需要添加代碼 mapper.registerModule(new JodaModule()),同時添加相應(yīng)的依賴 jar 包
    清單 5. joda 的 配置信息
<dependency> 
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-joda</artifactId> 
    <version>2.9.1</version> 
</dependency>

2. 泛型反序列化

Jackson 對泛型反序列化也提供很好的支持。

對于 List 類型 ,可以調(diào)用 constructCollectionType 方法來序列化,也可以構(gòu)造 TypeReference 來序列化。
清單 6 . List 泛 型使用示例

CollectionType javaType = mapper.getTypeFactory() 
                    .constructCollectionType(List.class, Person.class); 
List<Person> personList = mapper.readValue(jsonInString, javaType); 
List<Person> personList = mapper.readValue(jsonInString, 
                            new TypeReference<List<Person>>(){});

對于 map 類型, 與 List 的實現(xiàn)方式相似。
清單 7 . Map 泛型使用示例

//第二參數(shù)是 map 的 key 的類型,第三參數(shù)是 map 的 value 的類型 
 MapType javaType =
    mapper.getTypeFactory().constructMapType(HashMap.class,String.class,
    Person.class); 
 Map<String, Person> personMap = mapper.readValue(jsonInString, javaType); 
 Map<String, Person> personMap = mapper.readValue(jsonInString, 
                     new TypeReference<Map<String, Person>>() {});

Array 和 Collection 的處理與 List,Map 相似,這里不再詳述。

3. 屬性可視化

是 java 對象的所有的屬性都被序列化和反序列化,換言之,不是所有屬性都可視化,默認的屬性可視化的規(guī)則如下:

若該屬性修飾符是 public,該屬性可序列化和反序列化。
若屬性的修飾符不是 public,但是它的 getter 方法和 setter 方法是 public,該屬性可序列化和反序列化。因為 getter 方法用于序列化, 而 setter 方法用于反序列化。
若屬性只有 public 的 setter 方法,而無 public 的 getter 方 法,該屬性只能用于反序列化。
若想更改默認的屬性可視化的規(guī)則,需要調(diào)用 ObjectMapper 的方法 setVisibility。

下面的示例使修飾符為 protected 的屬性 name 也可以序列化和反序列化。

清單 8 . 屬性可視化示例

mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY); 
public class Person { 
    public int age; 
    protected String name; 
} 
PropertyAccessor 支持的類型有:
ALL,CREATOR,FIELD,GETTER,IS_GETTER,NONE,SETTER 
Visibility 
支持的類型有:ANY,DEFAULT,NON_PRIVATE,NONE,PROTECTED_AND_PUBLIC,PUBLIC_ONLY

4. 屬性過濾

在將 Java 對象序列化為 json 時 ,有些屬性需要過濾掉,不顯示在 json 中 , Jackson 有多種實現(xiàn)方法。

  • 注解方式, 可以用 @JsonIgnore 過濾單個屬性或用 @JsonIgnoreProperties 過濾多個屬性,示例如下:

清單 9 . 屬性過濾示例一

@JsonIgnore 
public int getAge() 
@JsonIgnoreProperties(value = { "age","birth_date" }) 
public class Person
  • addMixIn 方法加注解方式@JsonIgnoreProperties。
    addMixIn 方法簽名如下:
    public ObjectMapper addMixIn(Class<?> target, Class<?> mixinSource);
    addMixIn 方法的作用是用 mixinSource 接口或類的注解會重寫 target 或 target 的子類型的注解。 用ixIn 設(shè)置 Person peixIn 的 @JsonIgnoreProperties("name")所重寫,最終忽略的屬性為 name,最終生成的 json 如下:
    {"birthDate":"2017/09/13","age":40}

  • SimpleBeanPropertyFilter 方式。這種方式比前兩種方式更加靈活,也更復(fù)雜一些。
    首先需要設(shè)置@JsonFilter 類或接口,其次設(shè)置 addMixIn,將@JsonFilter 作用于 java 對象上,最后調(diào)用 SimpleBeanPropertyFilter 的 serializeAllExcept 方法或重寫 S impleBeanPropertyFilter 的 serializeAsField 方法來過濾相關(guān)屬性。示例如下:

清單 11 . 屬性過濾示例三

//設(shè)置 Filter 類或接口 
 @JsonFilter("myFilter") 
public interface MyFilter {} 
//設(shè)置 addMixIn 
mapper.addMixIn(Person.class, MyFilter.class); 
//調(diào)用 SimpleBeanPropertyFilter 的 serializeAllExcept 方法 
SimpleBeanPropertyFilter newFilter = 
                    SimpleBeanPropertyFilter.serializeAllExcept("age"); 
//或重寫 SimpleBeanPropertyFilter 的 serializeAsField 方法 
 SimpleBeanPropertyFilter newFilter = new SimpleBeanPropertyFilter() { 
    @Override 
    public void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer) throws Exception { 
        if (!writer.getName().equals("age")) { 
            writer.serializeAsField(pojo, jgen, provider); 
        } 
    } 
}; 
//設(shè)置 FilterProvider 
FilterProvider filterProvider = new SimpleFilterProvider()      
                                    .addFilter("myFilter", newFilter); 
mapper.setFilterProvider(filterProvider).writeValueAsString(person);

5. 自定義序列化和反序列化

當(dāng) Jackson 默認序列化和反序列化的類不能滿足實際需要,可以自定義新的序列化和反序列化的類。

  • 自定義序列化類。自定義的序列化類需要直接或間接繼承 StdSerializer 或 JsonSerializer,同時需要利用 JsonGenerator 生成 json,重寫方法 serialize,示例如下:

清單 12 . 自定義序列化

public class CustomSerializer extends StdSerializer<Person> { 
    @Override 
    public void serialize(Person person, JsonGenerator jgen, SerializerProvider provider) throws IOException { 
        jgen.writeStartObject(); 
        jgen.writeNumberField("age", person.getAge()); 
        jgen.writeStringField("name", person.getName()); 
        jgen.writeEndObject(); 
    } 
}

JsonGenerator 有多種 write 方法以支持生成復(fù)雜的類型的 json,比如 writeArray,writeTree 等 。若想單獨創(chuàng)建 JsonGenerator,可以通過 JsonFactory() 的 createGenerator。

  • 自定義反序列化類。自定義的反序列化類需要直接或間接繼承 StdDeserializer 或 StdDeserializer,同時需要利用 JsonParser 讀取 json,重寫方法 deserialize,示例如下:

清單 13 . 自定義序列化

public class CustomDeserializer extends StdDeserializer<Person> { 
     @Override 
     public Person deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { 
        JsonNode node = jp.getCodec().readTree(jp); 
        Person person = new Person(); 
        int age = (Integer) ((IntNode) node.get("age")).numberValue(); 
        String name = node.get("name").asText(); 
        person.setAge(age); 
        person.setName(name); 
        return person; 
    } 
}

JsonParser 提供很多方法來讀取 json 信息, 如 isClosed(), nextToken(), getValueAsString()等。若想單獨創(chuàng)建 JsonParser,可以通過 JsonFactory() 的 createParser。

  • 定義好自定義序列化類和自定義反序列化類,若想在程序中調(diào)用它們,還需要注冊到 ObjectMapper 的 Module,示例如下:

清單 14 . 注 冊 M odule 示例

SimpleModule module = new SimpleModule("myModule"); 
module.addSerializer(new CustomSerializer(Person.class)); 
module.addDeserializer(Person.class, new CustomDeserializer()); 
mapper.registerModule(module); 
//也可通過注解方式加在 java 對象的屬性,方法或類上面來調(diào)用它們, 
@JsonSerialize(using = CustomSerializer.class) 
@JsonDeserialize(using = CustomDeserializer.class) 
public class Person

6. 樹模型處理

Jackson 也提供了樹模型(tree model)來生成和解析 json。若想修改或訪問 json 部分屬性,樹模型是不錯的選擇。樹模型由 JsonNode 節(jié)點組成。程序中常常使用 ObjectNode,ObjectNode 繼承于 JsonNode,示例如下:

清單 15 . ObjectNode 生成和解析 json 示例

ObjectMapper mapper = new ObjectMapper(); 
//構(gòu)建 ObjectNode 
ObjectNode personNode = mapper.createObjectNode(); 
//添加/更改屬性 
personNode.put("name","Tom"); 
personNode.put("age",40); 
ObjectNode addressNode = mapper.createObjectNode(); 
addressNode.put("zip","000000"); 
addressNode.put("street","Road NanJing"); 
//設(shè)置子節(jié)點 
personNode.set("address",addressNode); 
//通過 path 查找節(jié)點 
JsonNode searchNode = personNode.path("street "); 
//刪除屬性 
((ObjectNode) personNode).remove("address"); 
//讀取 json 
JsonNode rootNode = mapper.readTree(personNode.toString()); 
//JsonNode 轉(zhuǎn)換成 java 對象 
Person person = mapper.treeToValue(personNode, Person.class); 
//java 對象轉(zhuǎn)換成 JsonNode 
JsonNode node = mapper.valueToTree(person);

四、總結(jié)

本文首先通過與其他 Java 的 json 的框架比較,介紹了 Jackson 的優(yōu)點,并且描述了 Jackson 的 核心模塊的組成,以及每個部分的作用。然后, 本文通過示例,講解 Jackson 的基本用法,介紹了 ObjectMapper 的 write 和 read 方法,ObjectMapper 的配置信息設(shè)定,以及 jackson-annotations 包下注釋的運用。最后,本文詳細的介紹了 Jackson 的高階用法,這也是本文的重點。這些高階用法包括不同類型的日期格式處理(普通日期的類型,jdk 8 的日期類型,joda 的日期類型),List 和 Map 等泛型的反序列化,屬性的可視化管理,Jackson 的 三種屬性過濾方式,自定義序列化和反序列化的實現(xiàn)以及樹模型的使用。通過本文的系統(tǒng)地講解,相信讀者對 Jackson 會有更深刻而全面的掌握。

參考資源
Jenkov.com 的 的 Jackson - ObjectMapper 部分 ,介紹 Jackson 的 ObjectMapper 的各種 write 和 read 方法 。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Jackson使用規(guī)范以及代碼示例 依賴包 Maven依賴: org.codehaus.jackson jacks...
    山石水壽閱讀 4,931評論 0 3
  • Java生態(tài)圈中有很多處理JSON和XML格式化的類庫,Jackson是其中比較著名的一個。雖然JDK自帶了XML...
    樂百川閱讀 28,280評論 6 23
  • 親愛的朋友,我想,關(guān)于問題,你必須明白以下幾點: 1.問題存在于我們的思想中。 2.當(dāng)心中的問題消失了,外界的問題...
    覺知中的帆閱讀 241評論 0 0
  • 敢于把自己從神壇上趕下來的人是真正的勇者,后人會為他樹立雕像;而不斷給自己樹立雕像的人心中充滿了虛弱和底氣不足
    馬刺兒閱讀 118評論 0 0
  • 大家早上好! 分享一個臣服的力量: 一直以來我抵觸別人生氣,看到別人生氣就有抵觸,要不自己也生氣,要不就會勸,結(jié)果...
    a惠蕓a自然規(guī)律閱讀 422評論 0 5

友情鏈接更多精彩內(nèi)容