天天看點

JSON序列化方案知識概括JSON序列化方案方案對比Jackson簡介Jackson使用統一配置使用注解日期處理對象集合屬性可視化屬性過濾自定義序列化類樹模型處理

JSON序列化方案知識概括

  • JSON序列化方案
  • 方案對比
  • Jackson簡介
  • Jackson使用
  • 統一配置
  • 使用注解
  • 日期處理
  • 對象集合
  • 屬性可視化
  • 屬性過濾
  • 自定義序列化類
  • 樹模型處理

JSON序列化方案

什麼是Jackson:

  • Jackson是比較主流的基于Java的JSON類庫,可用于Json和XML與JavaBean之間的序列化和反序列化。
  • 沒看錯,Jackson也可以處理JavaBean與XML之間的轉換,基于jackson-dataformat-xml元件,而且比較JDK自帶XML實作更加高效和安全。而我們使用比較多的是處理JSON與JavaBean之間的功能。
  • Jackson主流到什麼程度?單從Maven倉庫中的統計來看,Jackson的使用量排位第一。而Spring

    Boot支援的三個JSON庫(Gson、Jackson、JSON-B)中,Jackson是首選預設庫。

  • Jackson也有以下特點:依賴少,簡單易用,解析大Json速度快、記憶體占用比較低、擁有靈活的API、友善擴充與定制。
  • Jackson的pom依賴:
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.2.2</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.2.2</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.2.2</version>
</dependency>
           
  • Jackson基本使用:

    ①Jackson核心類ObjectMapper

    ②Jackson提供了三種JSON的處理方式,分别是:資料綁定、JSON樹模型、流式API。

    ③其中前兩項功能都是基于ObjectMapper來實作的,

    ④而流式API功能則需要基于更底層的JsonGenerator和JsonParser來實作。

FastJson為何:

  • 官網的介紹:FastJson是阿裡巴巴的開源JSON解析庫,它可以解析JSON格式的字元串,支援将Java

    Bean序列化為JSON字元串,也可以從JSON字元串反序列化到JavaBean。

  • FastJson是Java程式員常用到的類庫之一,相信點開這個頁面的你,也肯定是程式員朋友。正如其名,“快”是其主要賣點。
  • FastJson的pom依賴:
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.4</version>
</dependency>
           
  • Fastjson中使用介紹:
一、Fastjson中的經常調用的方法:
1 public static final Object parse(String text);   // 把JSON文本parse為JSONObject或者JSONArray 
2 public static final JSONObject parseObject(String text);   // 把JSON文本parse成JSONObject 
3 public static final T parseObject(String text, Class clazz);   // 把JSON文本parse為JavaBean 
4 public static final JSONArray parseArray(String text);   // 把JSON文本parse成JSONArray 
5 public static final List parseArray(String text, Class clazz);   //把JSON文本parse成JavaBean集合 
6 public static final String toJSONString(Object object);   // 将JavaBean序列化為JSON文本 
7 public static final String toJSONString(Object object, boolean prettyFormat);   // 将JavaBean序列化為帶格式的JSON文本 
8 public static final Object toJSON(Object javaObject);   //将JavaBean轉換為JSONObject或者JSONArray。

 

二、Fastjson字元串轉List<Map<String,Object>>(), 或者List<String>()的用法:
List<Map<String, Object>> list = JSONObject.parseObject(respJson, new TypeReference<List<Map<String, Object>>>() {});

 

三、Fastjson的SerializerFeature序列化屬性:
QuoteFieldNames———-輸出key時是否使用雙引号,預設為true
WriteMapNullValue——–是否輸出值為null的字段,預設為false
WriteNullNumberAsZero—-數值字段如果為null,輸出為0,而非null
WriteNullListAsEmpty—–List字段如果為null,輸出為[],而非null
WriteNullStringAsEmpty—字元類型字段如果為null,輸出為”“,而非null
WriteNullBooleanAsFalse–Boolean字段如果為null,輸出為false,而非null

例如:JSON.toJSONString(resultMap, SerializerFeature.WriteMapNullValue); 
           

GSON介紹:

  • GSON是Google提供的用來在Java對象和JSON資料之間進行映射的Java類庫。可以将一個Json字元轉成一個Java對象,或者将一個Java轉化為Json字元串。
  • 特點:

    ①快速、高效

    ②代碼量少、簡潔

    ③面向對象

    ④資料傳遞和解析友善

  • Gson的pom依賴:
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.0</version>
</dependency>
           
  • Google的Gson包的使用簡介

    ①Gson類:解析json的最基礎的工具類

    ②JsonParser類:解析器來解析JSON到JsonElements的解析樹

    ③JsonElement類:一個類代表的JSON元素

    ④JsonObject類:JSON對象類型

    ⑤JsonArray類:JsonObject數組

    ⑥TypeToken類:用于建立type,比如泛型List<?>

方案對比

為什麼不使用fastjson?

  • 你寫個bean,然後屬性裡分别有包含_(下劃線開頭、#開頭)之類的屬性,序列化為json時,出現屬性丢失,那麼自然你也無法反序列化回來,據說最新的1.2.14已經改正了這個bug(我沒測試不做評價);
  • 翻閱fastjson的源碼,你會發現有很多寫死的代碼,比如:針對spring之類的架構的各種處理,都是用classload判斷是否存在這種類名fastjson/SerializeConfig.java at master · alibaba/fastjson GitHub;那麼這是什麼意思呢?意思就是如果你用spring的那種思想,自己寫了個類似的功能,因為你這個項目裡沒有spring的那個類,那麼用起來就有一堆bug;當然不僅限于這些,還有很多,比如ASM位元組碼織入部分fastjson/ASMSerializerFactory.java at master · alibaba/fastjson ·GitHub(溫少的ASM方面水準是個半吊子),看源碼的話,能發現的缺點數不勝數。
  • 其解析json主要是用的String類substring這個方法fastjson/JSONScanner.java at master ·alibaba/fastjson GitHub,是以解析起來非常“快”,因為申請記憶體次數很少。但是因為jdk1.7之前substring的實作并沒有new一個新對象,在使用的時候,如果解析的json非常多,稍不注意就會出現記憶體洩漏(比如一個40K的json,你在對象裡引用了裡邊的一個key,即使這個key隻有2位元組,也會導緻這40K的json無法被垃圾回收器回收),這也是“快”帶來的負面效果。而且這還不算,在jdk1.7以上版本對string的substring方法做了改寫,改成了重新new一個string的方式,于是這個“快”的優勢也不存在了。總結:fastjson就是一個代碼品質較差的國産類庫,用很多投機取巧的的做法去實作所謂的“快”,而失去了原本應該相容的java特性,對json标準遵循也不嚴格,自然很難在國際上流行。
  • 連結:fastjson這麼快老外為啥還是熱衷 jackson

Jackson Vs.Gson:

  • 測試結果:
資料集 gson耗時 Jackson耗時
10w 1366 138
20w 2720 165
30w 4706 332
40w 9526 317
50w 本機OOM 363

Jackson是世界最好的JSON庫:

  • Jackson是一個簡單的、功能強大的、基于Java的應用庫。
  • 它可以很友善完成Java對象和Json對象(xml文檔or其它格式)進行互轉。
  • Jackson社群相對比較活躍,更新速度也比較快。
  • Jackson庫有如下幾大特性:

    ①高性能且穩定:低記憶體占用,對大/小JSON串,大/小對象的解析表現均很優秀

    ②流行度高:是很多流行架構的預設選擇

    ③容易使用:提供高層次的API,極大簡化了日常使用案例

    ④無需自己手動建立映射:内置了絕大部分序列化時和Java類型的映射關系

    ⑤幹淨的JSON:建立的JSON具有幹淨、緊湊、體積小等特點

    ⑥第三方依賴:僅依賴于JDK

    ⑦Spring生态加持:jackson是Spring家族的預設JSON/XML解析器(明白了吧,學完此專欄你對Spring都能更親近些了,一舉兩得)

org.json與json-lib的差別:

  • org.json 是JSON國際組織官方推出的标準json解析方案,已經被 android sdk納入到标準内置類庫,依賴項少,但直至API17版本SDK中,僅支援JSONObject與JSONArray、Map、List、String、Boolean、Integer等基本類型對象,适合簡單開發調用。
  • json-lib 是另外一個開源項目,需要自行下載下傳,依賴項較多,除org.json所支援的對象外,還直接支援基本類型數組、對象數組、json<->xml 格式轉換、json<->自定義Class(Bean) 轉換等功能,适合複雜的擴充調用開發。
  • 調查資料發現,json-lib比較老舊,由于自身的衆多bug、依賴多、API繁瑣、處理效率低下等問題,官方已經停止維護并逐漸被淘汰。轉而一個稱為jackson的項目,因為效率高、依賴少,社群活躍,文檔齊全,很快成為替代json-lib的主流。

Jackson簡介

Jackson三大核心子產品:

  • core module(核心子產品) 是擴充子產品建構的基礎。Jackson目前有3個核心子產品:

    ①說明:核心子產品的groupId均為:<groupId>com.fasterxml.jackson.core</groupId>,artifactId見下面各子產品所示

  • Streaming流處理子產品(jackson-core):定義底層處理流的API:JsonPaser和JsonGenerator等,并包含特定于json的實作。

  • Annotations标準注解子產品(jackson-annotations):包含标準的Jackson注解

  • Databind資料綁定子產品(jackson-databind):在streaming包上實作資料綁定(和對象序列化)支援;它依賴于上面的兩個子產品,也是Jackson的高層API(如ObjectMapper)所在的子產品

  • 實際應用級開發中,我們隻會使用到Databind資料綁定子產品

    ,是以它是本系列重中之重。
  • 連結:史上最全的Jackson架構使用教程

使用 Jackson 的核心子產品的 jar 包需要在 pom.xml 中添加如下資訊:

  • jackson-databind 依賴 jackson-core 和 jackson-annotations

  • 當添加 jackson-databind 之後, jackson-core 和 jackson-annotations 也随之添加到Java 項目工程中。在添加相關依賴包之後,就可以使用 Jackson
<dependency> 
	<groupId>com.fasterxml.jackson.core</groupId> 
	<artifactId>jackson-databind</artifactId> 
	<version>2.9.1</version> 
</dependency>
           

Jackson使用

序列化和反序列化簡單使用案例:

  • Jackson 最常用的 API 就是基于"對象綁定" 的 ObjectMapper。

    下面是一個 ObjectMapper的使用的簡單示例。
  • 準備一個名稱為 Person 的 Java 對象:
public class Person {
    // 正常case
    private String name;
    // 空對象case
    private Integer age;
    // 日期轉換case
    private Date date;
    // 預設值case
    private int height;
}
           
  • 使用示例:
@Test
public void test1() throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    // 造資料
    Person person = new Person();
    person.setName("Tom");
    person.setAge(40);
    person.setDate(new Date());
    System.out.println("序列化");
    String jsonString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(person);
    System.out.println(jsonString);
    System.out.println("反序列化");
    Person deserializedPerson = mapper.readValue(jsonString, Person.class);
    System.out.println(deserializedPerson);
}
           
  • 列印輸出:
序列化:
{
"name" : "Tom",
"age" : 40,
"date" : 1594634846647,
"man" : null,
"height" : 0
}

反序列化:
JackSonTest.Person(name=Tom, age=40, 
date=Mon Jul 13 18:07:26 CST 2020, man=null, height=0)
           
  • ObjectMapper 通過 writeValue 系列方法将 java 對象序列化為 json,并将 json存儲成不同的格式,String(writeValueAsString),ByteArray(writeValueAsString),Writer, File,OutStream 和 DataOutput。

  • ObjectMapper 通過 readValue 系列方法從不同的資料源像 String , Byte Array,Reader,File,URL, InputStream 将 json 反序列化為 java 對象。

統一配置

簡介:

  • 在調用 writeValue 或調用 readValue 方法之前,往往需要設定 ObjectMapper的相關配置資訊。這些配置資訊應用 java 對象的所有屬性上

    。示例如下:
//在反序列化時忽略在 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);

//在序列化時自定義時間日期格式
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

//在序列化時忽略值為 null 的屬性
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

//在序列化時忽略值為預設值的屬性
mapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_DEFAULT);
           
  • 更多配置資訊可以檢視 Jackson 的 DeserializationFeature,SerializationFeature 和

    Include。

  • 重新運作單元測試,列印輸出:
序列化:
{
  "name" : "Tom",
  "age" : 40,
  "date" : "2020-07-26 18:46:51"
}

反序列化:
JackSonTest.Person(name=Tom, age=40, date=Sun Jul 26 18:46:51 CST 2020, height=0)
           

使用注解

簡介:

  • Jackson 根據它的預設方式序列化和反序列化 java 對象,若根據實際需要,靈活的調整它的預設方式,可以使用 Jackson的注解

    。常用的注解及用法如下:
注解 用法
@JsonProperty 用于屬性,把屬性的名稱序列化時轉換為另外一個名稱。示例:@JsonProperty(“birth_date”) private Date birthDate
@JsonIgnore 可用于字段、getter/setter、構造函數參數上,作用相同,都會對相應的字段産生影響。使相應字段不參與序列化和反序列化。
@JsonIgnoreProperties 該注解是類注解。該注解在Java類和JSON不完全比對的時候使用。
@JsonFormat 用于屬性或者方法,把屬性的格式序列化時轉換成指定的格式。示例:@JsonFormat(timezone = “GMT+8”, pattern = “yyyy-MM-dd HH:mm”) public Date getBirthDate()
@JsonPropertyOrder 用于類, 和 @JsonProperty 的index屬性類似,指定屬性在序列化時 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; }
@JsonNaming 類注解。序列化的時候該注解可将駝峰命名的字段名轉換為下劃線分隔的小寫字母命名方式。反序列化的時候可以将下劃線分隔的小寫字母轉換為駝峰命名的字段名。示例:@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
@JsonRootName 類注解。需開啟mapper.enable(SerializationFeature.WRAP_ROOT_VALUE),用于序列化時輸出帶有根屬性名稱的 JSON 串,形式如 {“root_name”:{“id”:1,“name”:“zhangsan”}}。但不支援該 JSON 串反序列化。
  • 使用示例:
// 用于類,指定屬性在序列化時 json 中的順序
@JsonPropertyOrder({"date", "user_name"})
// 批量忽略屬性,不進行序列化
@JsonIgnoreProperties(value = {"other"})
// 用于序列化與反序列化時的駝峰命名與小寫字母命名轉換
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public static class User {
    @JsonIgnore
    private Map<String, Object> other = new HashMap<>();

    // 正常case
    @JsonProperty("user_name")
    private String userName;
    // 空對象case
    private Integer age;
    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
    // 日期轉換case
    private Date date;
    // 預設值case
    private int height;

    public User() {
    }

    // 反序列化執行構造方法
    @JsonCreator
    public User(@JsonProperty("user_name") String userName) {
        System.out.println("@JsonCreator 注解使得反序列化自動執行該構造方法 " + userName);
        // 反序列化需要手動指派
        this.userName = userName;
    }

    @JsonAnySetter
    public void set(String key, Object value) {
        other.put(key, value);
    }

    @JsonAnyGetter
    public Map<String, Object> any() {
        return other;
    }
    // 本文預設省略getter、setter方法
}
           
  • 單元測試:
@Test
public void test3() throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    // 造資料
    Map<String, Object> map = new HashMap<>();
    map.put("user_name", "Tom");
    map.put("date", "2020-07-26 19:28:44");
    map.put("age", 100);
    map.put("demoKey", "demoValue");
    String jsonString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(map);
    System.out.println(jsonString);
    System.out.println("反序列化");
    User user = mapper.readValue(jsonString, User.class);
    System.out.println(user);
    System.out.println("序列化");
    jsonString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(user);
    System.out.println(jsonString);
}
           
  • 列印輸出:
{
  "date" : "2020-07-26 19:28:44",
  "demoKey" : "demoValue",
  "user_name" : "Tom",
  "age" : 100
}
反序列化
@JsonCreator 注解使得反序列化自動執行該構造方法Tom
JackSonTest.User(other={demoKey=demoValue}, userName=Tom, age=100, date=Sun Jul 26 19:28:44 CST 2020, height=0)
序列化
{
  "date" : "2020-07-26 19:28:44",
  "user_name" : "Tom",
  "age" : 100,
  "height" : 0,
  "demoKey" : "demoValue"
}
           

日期處理

簡介:

  • 不同類型的日期類型,JackSon 的處理方式也不同。
  • 普通日期

    ①對于日期類型為 java.util.Calendar, java.util.GregorianCalendar, java.sql.Date, java.util.Date, java.sql.Timestamp,若不指定格式,在 json 檔案中将序列化為 long 類型的資料。顯然這種預設格式,可讀性差,轉換格式是必要的。

    ②JackSon 有很多方式轉換日期格式。

    <1>注解方式,使用 @JsonFormat 注解指定日期格式。

    <2>ObjectMapper 方式,調用 ObjectMapper 的方法 setDateFormat,将序列化為指定格式的 string 類型的資料。

  • Local日期

    ①對于日期類型為 java.time.LocalDate, java.time.LocalDateTime,還需要添加代碼 mapper.registerModule(new JavaTimeModule()),同時添加相應的依賴 jar 包。

    ②導入依賴:

    <1>對于 Jackson 2.5 以下版本,需要添加代碼 mapper.registerModule(new JSR310Module ())。

<dependency> 
  <groupId>com.fasterxml.jackson.datatype</groupId> 
  <artifactId>jackson-datatype-jsr310</artifactId> 
  <version>2.9.1</version> 
</dependency>
           
-----------------------使用示例:--------------------------
@Data
public static class Student {
    // 正常case
    private String name;
    // 日期轉換case
    private LocalDateTime date;
}

@Test
public void test4() throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    // 必須添加對LocalDate的支援
    mapper.registerModule(JavaTimeModule());
    // 造資料
    Student student = new Student();
    student.setName("Tom");
    student.setDate(LocalDateTime.now());
    System.out.println("序列化");
    String jsonString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(student);
    System.out.println(jsonString);
    System.out.println("反序列化");
    Student deserializedPerson = mapper.readValue(jsonString, Student.class);
    System.out.println(deserializedPerson);
}

private Module JavaTimeModule() {
    JavaTimeModule module = new JavaTimeModule();
    String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    String DATE_FORMAT = "yyyy-MM-dd";
    String TIME_FORMAT = "HH:mm:ss";
    module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)));
    module.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DATE_FORMAT)));
    module.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern((TIME_FORMAT))));
    module.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)));
    module.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DATE_FORMAT)));
    module.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(TIME_FORMAT)));
    return module;
}
-----------------------列印輸出:--------------------------
序列化
{
  "name" : "Tom",
  "date" : "2020-07-26 23:10:42"
}
反序列化
JackSonTest.Student(name=Tom, date=2020-07-26T23:10:42)
           
  • Joda日期

    ①對于日期類型為 org.joda.time.DateTime,還需要添加代碼 mapper.registerModule(new JodaModule()),同時添加相應的依賴 jar 包

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

對象集合

簡介:

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

    ①對于 List 類型 ,可以調用 constructCollectionType 方法來序列化,也可以構造 TypeReference 來序列化。

-----------------------使用示例:-----------------------
@Test
public void test5() throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    CollectionType javaType = mapper.getTypeFactory().constructCollectionType(List.class, Person.class);
    // 造資料
    List<Person> list = new ArrayList<>();
    for (int i = 0; i < 3; i++) {
        Person person = new Person();
        person.setName("Tom");
        person.setAge(new Random().nextInt(100));
        person.setDate(new Date());
        list.add(person);
    }
    System.out.println("序列化");
    String jsonInString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(list);
    System.out.println(jsonInString);
    System.out.println("反序列化:使用 javaType");
    List<Person> personList = mapper.readValue(jsonInString, javaType);
    System.out.println(personList);
    System.out.println("反序列化:使用 TypeReference");
    List<Person> personList2 = mapper.readValue(jsonInString, new TypeReference<List<Person>>() {
    });
    System.out.println(personList2);
}
-----------------------列印輸出:-----------------------
序列化
[ {
  "name" : "Tom",
  "age" : 33,
  "date" : 1595778639371,
  "height" : 0
}, {
  "name" : "Tom",
  "age" : 3,
  "date" : 1595778639371,
  "height" : 0
}, {
  "name" : "Tom",
  "age" : 36,
  "date" : 1595778639371,
  "height" : 0
} ]
反序列化:使用javaType
[JackSonTest.Person(name=Tom, age=33, date=Sun Jul 26 23:50:39 CST 2020, height=0), JackSonTest.Person(name=Tom, age=3, date=Sun Jul 26 23:50:39 CST 2020, height=0), JackSonTest.Person(name=Tom, age=36, date=Sun Jul 26 23:50:39 CST 2020, height=0)]
反序列化:使用TypeReference
[JackSonTest.Person(name=Tom, age=33, date=Sun Jul 26 23:50:39 CST 2020, height=0), JackSonTest.Person(name=Tom, age=3, date=Sun Jul 26 23:50:39 CST 2020, height=0), JackSonTest.Person(name=Tom, age=36, date=Sun Jul 26 23:50:39 CST 2020, height=0)]
           
  • Map

    ①對于 map 類型, 與 List 的實作方式相似。

-------------------使用示例:-------------------------
@Test
public void test6() throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    //第二參數是 map 的 key 的類型,第三參數是 map 的 value 的類型
    MapType javaType = mapper.getTypeFactory().constructMapType(HashMap.class, String.class, Person.class);
    // 造資料
    Map<String, Person> map = new HashMap<>();
    for (int i = 0; i < 3; i++) {
        Person person = new Person();
        person.setName("Tom");
        person.setAge(new Random().nextInt(100));
        person.setDate(new Date());
        map.put("key" + i, person);
    }
    System.out.println("序列化");
    String jsonInString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(map);
    System.out.println(jsonInString);
    System.out.println("反序列化: 使用 javaType");
    Map<String, Person> personMap = mapper.readValue(jsonInString, javaType);
    System.out.println(personMap);
    System.out.println("反序列化: 使用 TypeReference");
    Map<String, Person> personMap2 = mapper.readValue(jsonInString, new TypeReference<Map<String, Person>>() {
    });
    System.out.println(personMap2);
}

----------------------列印輸出:----------------------
序列化
{
  "key1" : {
    "name" : "Tom",
    "age" : 95,
    "date" : 1595778740606,
    "height" : 0
  },
  "key2" : {
    "name" : "Tom",
    "age" : 27,
    "date" : 1595778740606,
    "height" : 0
  },
  "key0" : {
    "name" : "Tom",
    "age" : 98,
    "date" : 1595778740606,
    "height" : 0
  }
}
反序列化: 使用 javaType
{key1=JackSonTest.Person(name=Tom, age=95, date=Sun Jul 26 23:52:20 CST 2020, height=0), key2=JackSonTest.Person(name=Tom, age=27, date=Sun Jul 26 23:52:20 CST 2020, height=0), key0=JackSonTest.Person(name=Tom, age=98, date=Sun Jul 26 23:52:20 CST 2020, height=0)}
反序列化: 使用 TypeReference
{key1=JackSonTest.Person(name=Tom, age=95, date=Sun Jul 26 23:52:20 CST 2020, height=0), key2=JackSonTest.Person(name=Tom, age=27, date=Sun Jul 26 23:52:20 CST 2020, height=0), key0=JackSonTest.Person(name=Tom, age=98, date=Sun Jul 26 23:52:20 CST 2020, height=0)}
           
  • Array 和 Collection 的處理與 List,Map 相似,這裡不再詳述。

屬性可視化

簡介:

  • JackSon 預設不是所有的屬性都可以被序列化和反序列化。預設的屬性可視化的規則如下:

    ①若該屬性修飾符是 public,該屬性可序列化和反序列化。

    ②若屬性的修飾符不是 public,但是它的 getter 方法和 setter 方法是 public,該屬性可序列化和反序列化。因為 getter 方法用于序列化, 而 setter 方法用于反序列化。

    ③若屬性隻有 public 的 setter 方法,而無 public 的 getter 方 法,該屬性隻能用于反序列化。

  • 若想更改預設的屬性可視化的規則,需要調用 ObjectMapper 的方法 setVisibility。
  • 下面的示例使修飾符為 protected 的屬性 name 也可以序列化和反序列化:
-----------------案例:---------------------
public static class People {
    public int age;
    protected String name;
}

@Test
public void test7() throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    // PropertyAccessor 支援的類型有 ALL,CREATOR,FIELD,GETTER,IS_GETTER,NONE,SETTER
    // Visibility 支援的類型有 ANY,DEFAULT,NON_PRIVATE,NONE,PROTECTED_AND_PUBLIC,PUBLIC_ONLY
    mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
    // 造資料
    People people = new People();
    people.name = "Tom";
    people.age = 40;
    System.out.println("序列化");
    String jsonString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(people);
    System.out.println(jsonString);
    System.out.println("反序列化");
    People deserializedPerson = mapper.readValue(jsonString, People.class);
    System.out.println(deserializedPerson);
}

-------------------列印輸出:-------------------
序列化
{
  "age" : 40,
  "name" : "Tom"
}
反序列化
JackSonTest.People(age=40, name=Tom)
           

屬性過濾

簡介:

  • 在将 Java 對象序列化為 json 時 ,有些屬性需要過濾掉,不顯示在 json 中 ,除了使用 @JsonIgnore過濾單個屬性或用 @JsonIgnoreProperties 過濾多個屬性之外, Jackson 還有通過代碼控制的方式。
  • 使用示例:
-------------------示例:-------------------
@JsonFilter("myFilter")
public interface MyFilter {
}

@Test
public void test8() throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();
    //設定 addMixIn
    mapper.addMixIn(Person.class, MyFilter.class);
    //調用 SimpleBeanPropertyFilter 的 serializeAllExcept 方法
    SimpleBeanPropertyFilter newFilter = SimpleBeanPropertyFilter.serializeAllExcept("age");
    //或重寫 SimpleBeanPropertyFilter 的 serializeAsField 方法
    SimpleBeanPropertyFilter newFilter2 = 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);
    // 造資料
    Person person = new Person();
    person.setName("Tom");
    person.setAge(40); // 該屬性将被忽略
    person.setDate(new Date());
    // 序列化
    String jsonString = mapper.setFilterProvider(filterProvider).writeValueAsString(person);
    System.out.println(jsonString);
}

-------------------列印輸出:-------------------
{"name":"Tom","date":1595780842754}
           

自定義序列化類

簡介:

  • 當 Jackson 預設序列化和反序列化的類不能滿足實際需要,可以自定義新的序列化和反序列化的類。
  • 自定義序列化類:自定義的序列化類需要直接或間接繼承 StdSerializer 或 JsonSerializer,同時需要利用 JsonGenerator 生成 json,重寫方法 serialize,示例如下:
public static class CustomSerializer extends StdSerializer<Person> {
    protected CustomSerializer() {
        super(Person.class);
    }

    @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.writeStringField("msg", "已被自定義序列化");
        jgen.writeEndObject();
    }
}
           
  • JsonGenerator 有多種 write 方法以支援生成複雜的類型的 json,比如 writeArray,writeTree 等

    。若想單獨建立 JsonGenerator,可以通過 JsonFactory() 的 createGenerator。

  • 自定義反序列化類:自定義的反序列化類需要直接或間接繼承 StdDeserializer 或 StdDeserializer,同時需要利用 JsonParser 讀取 json,重寫方法 deserialize,示例如下:
public static class CustomDeserializer extends StdDeserializer<Person> {
    protected CustomDeserializer() {
        super(Person.class);
    }

    @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,示例如下:
@Test
public void test9() throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    // 生成 module
    SimpleModule module = new SimpleModule("myModule");
    module.addSerializer(new CustomSerializer());
    module.addDeserializer(Person.class, new CustomDeserializer());
    // 注冊 module
    mapper.registerModule(module);
    // 造資料
    Person person = new Person();
    person.setName("Tom");
    person.setAge(40);
    person.setDate(new Date());
    System.out.println("序列化");
    String jsonString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(person);
    System.out.println(jsonString);
    System.out.println("反序列化");
    Person deserializedPerson = mapper.readValue(jsonString, Person.class);
    System.out.println(deserializedPerson);
}
           
  • 或者也可通過注解方式加在 java 對象的屬性,方法或類上面來調用它們:
@JsonSerialize(using = CustomSerializer.class)
@JsonDeserialize(using = CustomDeserializer.class)
           

樹模型處理

簡介:

  • Jackson 也提供了樹模型(tree model)來生成和解析 json。若想修改或通路 json部分屬性,樹模型是不錯的選擇。樹模型由 JsonNode 節點組成。程式中常常使用 ObjectNode,ObjectNode 繼承于JsonNode,示例如下:
-------------------示例:-----------------------
@Test
public void test10() throws IOException {
    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);
    System.out.println("建構 ObjectNode:\n" + personNode.toString());
    //通過 path 查找節點
    JsonNode searchNode = personNode.path("name");
    System.out.println("查找子節點 name:\n" + searchNode.asText());
    //删除屬性
    ((ObjectNode) personNode).remove("address");
    System.out.println("删除後的 ObjectNode:\n" + personNode.toString());
    //讀取 json
    JsonNode rootNode = mapper.readTree(personNode.toString());
    System.out.println("Json 轉 JsonNode:\n" + rootNode);
    //JsonNode 轉換成 java 對象
    Person person = mapper.treeToValue(personNode, Person.class);
    System.out.println("JsonNode 轉對象:\n" + person);
    //java 對象轉換成 JsonNode
    JsonNode node = mapper.valueToTree(person);
    System.out.println("對象轉 JsonNode:\n" + node);
}

----------------------列印輸出:---------------------
建構 ObjectNode:
{"name":"Tom","age":40,"address":{"zip":"000000","street":"Road NanJing"}}
查找子節點 name:
Tom
删除後的 ObjectNode:
{"name":"Tom","age":40}
Json 轉 JsonNode:
{"name":"Tom","age":40}
JsonNode 轉對象:
JackSonTest.Person(name=Tom, age=40, date=null, height=0)
對象轉 JsonNode:
{"name":"Tom","age":40,"date":null,"height":0}