1. 程式人生 > >【Jackson 框架】序列化和反序列化

【Jackson 框架】序列化和反序列化

Jackson 是當前用的比較廣泛的,用來序列化和反序列化 json 的 Java 的開源框架。Jackson 社 區相對比較活躍,更新速度也比較快, 從 Github 中的統計來看,Jackson 是最流行的 json 解析器之一 。 Spring MVC 的預設 json 解析器便是 Jackson。 Jackson 優點很多。 Jackson 所依賴的 jar 包較少 ,簡單易用。與其他 Java 的 json 的框架 Gson 等相比, Jackson 解析大的 json 檔案速度比較快;Jackson 執行時佔用記憶體比較低,效能比較好;Jackson 有靈活的 API,可以很容易進行擴充套件和定

制。

Jackson 的 1.x 版本的包名是 org.codehaus.jackson ,當升級到 2.x 版本時,包名變為 com.fasterxml.jackson,本文討論的內容是基於最新的 Jackson 的 2.9.1 版本。

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

  • jackson-core,核心包,提供基於"流模式"解析的相關 API,它包括 JsonPaser 和 JsonGenerator。 Jackson 內部實現正是通過高效能的流模式 API 的 JsonGenerator 和 JsonParser 來生成和解析 json。
  • jackson-annotations,註解包,提供標準註解功能;
  • jackson-databind ,資料繫結包, 提供基於"物件繫結" 解析的相關 API ( ObjectMapper ) 和"樹模型" 解析的相關 API (JsonNode);基於"物件繫結" 解析的 API 和"樹模型"解析的 API 依賴基於"流模式"解析的 API。

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

Jackson 的 基本用法

若想在 Java 程式碼中使用 Jackson 的核心模組的 jar 包 ,需要在 pom.xml 中新增如下資訊。

清單 1.在 pom.xml 的 Jackson 的配置資訊

1

2

3

4

5

<dependency>

<groupId>com.fasterxml.jackson.core</groupId>

<artifactId>jackson-databind</artifactId>

<version>2.9.1</version>

</dependency>

jackson-databind 依賴 jackson-core 和 jackson-annotations,當新增 jackson-databind 之後, jackson-core 和 jackson-annotations 也隨之新增到 Java 專案工程中。在新增相關依賴包之後,就可以使用 Jackson。

ObjectMapper 的 使用

Jackson 最常用的 API 就是基於"物件繫結" 的 ObjectMapper。下面是一個 ObjectMapper 的使用的簡單示例。

清單 2 . ObjectMapper 使用示例

1

2

3

4

5

6

7

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 系列方法從不同的資料來源像 String , Byte Array, Reader,File,URL, InputStream 將 json 反序列化為 java 物件。

資訊配置

在呼叫 writeValue 或呼叫 readValue 方法之前,往往需要設定 ObjectMapper 的相關配置資訊。這些配置資訊應用 java 物件的所有屬性上。示例如下:

清單 3 . 配置資訊使用示例

1

2

3

4

5

6

7

8

9

//在反序列化時忽略在 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。

Jackson 的 註解的使用

Jackson 根據它的預設方式序列化和反序列化 java 物件,若根據實際需要,靈活的調整它的預設方式,可以使用 Jackson 的註解。常用的註解及用法如下。

表 1. Jackson 的 常用註解

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

在瞭解 Jackson 的基本用法後,下面詳細地介紹它的一些高階應用。

Jackson 的 高階應用

格式處理(含日期格式)

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

  • 對於日期型別為 java.util.Calendar,java.util.GregorianCalendar,java.sql.Date,java.util.Date,java.sql.Timestamp,若不指定格式, 在 json 檔案中將序列化 為 long 型別的資料。顯然這種預設格式,可讀性差,轉換格式是必要的。Jackson 有 很多方式轉換日期格式。
  • 註解方式,請參照" Jackson 的註解的使用"的@ JsonFormat 的示例。
  • ObjectMapper 方式,呼叫 ObjectMapper 的方法 setDateFormat,將序列化為指定格式的 string 型別的資料。
  • 對於日期型別為 java.time.LocalDate,還需要新增程式碼 mapper.registerModule(new JavaTimeModule()),同時新增相應的依賴 jar 包

清單 4 . JSR31 0 的配置資訊

1

2

3

4

5

<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()),同時新增相應的依賴 jar 包

清單 5. joda 的 配置資訊

1

2

3

4

5

<dependency>

<groupId>com.fasterxml.jackson.datatype</groupId>

<artifactId>jackson-datatype-joda</artifactId>

<version>2.9.1</version>

</dependency>

泛型反序列化

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

  • 對於 List 型別 ,可以呼叫 constructCollectionType 方法來序列化,也可以構造 TypeReference 來序列化。

清單 6 . List 泛 型使用示例

1

2

3

4

5

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 的實現方式相似。

清單 7 . Map 泛型使用示例

1

2

3

4

5

6

7

8

//第二引數是 map 的 key 的型別,第三引數是 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 相似,這裡不再詳述。

屬性視覺化

是 java 物件的所有的屬性都被序列化和反序列化,換言之,不是所有屬性都視覺化,預設的屬性視覺化的規則如下:

  • 若該屬性修飾符是 public,該屬性可序列化和反序列化。
  • 若屬性的修飾符不是 public,但是它的 getter 方法和 setter 方法是 public,該屬性可序列化和反序列化。因為 getter 方法用於序列化, 而 setter 方法用於反序列化。
  • 若屬性只有 public 的 setter 方法,而無 public 的 getter 方 法,該屬性只能用於反序列化。

若想更改預設的屬性視覺化的規則,需要呼叫 ObjectMapper 的方法 setVisibility。

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

清單 8 . 屬性視覺化示例

1

2

3

4

5

6

7

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 支援的型別有 A

NY,DEFAULT,NON_PRIVATE,NONE,PROTECTED_AND_PUBLIC,PUBLIC_ONLY

屬性過濾

在將 Java 物件序列化為 json 時 ,有些屬性需要過濾掉,不顯示在 json 中 , Jackson 有多種實現方法。

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

清單 9 . 屬性過濾示例一

1

2

3

4

@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 設定

Person peixIn 的 @JsonIgnoreProperties("name")所重寫,最終忽略的屬性為 name,最終生成的 json 如下:

{"birthDate":"2017/09/13","age":40}

  • SimpleBeanPropertyFilter 方式。這種方式比前兩種方式更加靈活,也更復雜一些。

首先需要設定@JsonFilter 類或介面,其次設定 addMixIn,將@JsonFilter 作用於 java 物件上,最後呼叫 SimpleBeanPropertyFilter 的 serializeAllExcept 方法或重寫 S impleBeanPropertyFilter 的 serializeAsField 方法來過濾相關屬性。示例如下:

清單 11 . 屬性過濾示例三

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

//設定 Filter 類或介面

@JsonFilter("myFilter")

public interface MyFilter {}

//設定 addMixIn

mapper.addMixIn(Person.class, MyFilter.class);

//呼叫 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);

}

}

};

//設定 FilterProvider

FilterProvider filterProvider = new SimpleFilterProvider()

.addFilter("myFilter", newFilter);

mapper.setFilterProvider(filterProvider).writeValueAsString(person);

自定義序列化和反序列化

當 Jackson 預設序列化和反序列化的類不能滿足實際需要,可以自定義新的序列化和反序列化的類。

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

清單 12 . 自定義序列化

1

2

3

4

5

6

7

8

9

10

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 方法以支援生成複雜的型別的 json,比如 writeArray,writeTree 等 。若想單獨建立 JsonGenerator,可以通過 JsonFactory() 的 createGenerator。

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

清單 13 . 自定義序列化

1

2

3

4

5

6

7

8

9

10

11

12

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()等。若想單獨建立 JsonParser,可以通過 JsonFactory() 的 createParser。

  • 定義好自定義序列化類和自定義反序列化類,若想在程式中呼叫它們,還需要註冊到 ObjectMapper 的 Module,示例如下:

清單 14 . 注 冊 M odule 示例

1

2

3

4

5

6

7

8

SimpleModule module = new SimpleModule("myModule");

module.addSerializer(new CustomSerializer(Person.class));

module.addDeserializer(Person.class, new CustomDeserializer());

mapper.registerModule(module);

也可通過註解方式加在 java 物件的屬性,方法或類上面來呼叫它們,

@JsonSerialize(using = CustomSerializer.class)

@JsonDeserialize(using = CustomDeserializer.class)

public class Person

樹模型處理

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

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

ObjectMapper mapper = new ObjectMapper();

//構建 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");

//設定子節點

personNode.set("address",addressNode);

//通過 path 查詢節點

JsonNode searchNode = personNode.path("street ");

//刪除屬性

((ObjectNode) personNode).remove("address");

//讀取 json

JsonNode rootNode = mapper.readTree(personNode.toString());

//JsonNode 轉換成 java 物件

Person person = mapper.treeToValue(personNode, Person.class);

//java 物件轉換成 JsonNode

JsonNode node = mapper.valueToTree(person);