1. 概述
在本快速教程中,我們將演示兩種不同的方法,使用 Jackson JSON 處理庫反序列化不可變 Java 對象。
2. 為什麼使用不可變對象?
不可變對象是指在創建之初,其狀態就保持不變的對象。這意味着無論用户調用該對象的方法,對象始終保持相同行為。
不可變對象在設計一個需要多線程環境的系統時非常有用,因為不可變性通常保證了線程安全性。
另一方面,不可變對象在我們需要處理外部來源的數據時也很有用。例如,可以是用户輸入或存儲中的數據。在這種情況下,保持接收到的數據不變並保護它免受意外或未預期的更改至關重要。
讓我們看看如何反序列化一個不可變對象。
3. 公共構造函數
讓我們考慮 Employee 類結構。它有兩個必需的字段:id 和 name,因此我們定義了一個 公共全參數構造函數,它具有與對象字段集匹配的一組參數:
public class Employee {
private final long id;
private final String name;
public Employee(long id, String name) {
this.id = id;
this.name = name;
}
// getters
}
這樣一來,我們會在創建對象時初始化所有對象字段。 最終修飾符在字段聲明中不會允許我們在將來更改其值。 為了使此對象可序列化,我們只需向構造函數添加幾個註釋:
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public Employee(@JsonProperty("id") long id, @JsonProperty("name") String name) {
this.id = id;
this.name = name;
}
讓我們更仔細地研究我們剛剛添加的註釋。
首先,@JsonCreator 告訴 Jackson 解序列化器 使用指定構造函數進行解序列化。
可以使用作為此註釋參數的兩個模式:PROPERTIES 和 DELEGATING。
PROPERTIES 在我們聲明全參數構造函數時最合適,而 DELEGATING 對於單參數構造函數可能很有用。
之後,我們需要使用 @JsonProperty 註釋構造函數參數, 將相應屬性的名稱作為註解值。 在此步驟中,我們必須非常小心,因為所有 屬性名稱必須與我們在序列化過程中使用的名稱匹配。
讓我們看看覆蓋 Employee 對象解序列化的簡單單元測試:
String json = "{\"name\":\"Frank\",\"id\":5000}";
Employee employee = new ObjectMapper().readValue(json, Employee.class);
assertEquals("Frank", employee.getName());
assertEquals(5000, employee.getId());
4. 私有構造函數和構建器
有時,對象會包含一組可選字段。 讓我們考慮另一個類結構,Person,它具有可選的age字段:
public class Person {
private final String name;
private final Integer age;
// getters
}
當存在大量此類字段時,公開構造函數可能會變得繁瑣。 換句話説,我們需要聲明構造函數中大量的參數,併為每個參數添加@JsonProperty註解。 結果,大量的重複聲明會使我們的代碼臃腫且難以閲讀。
這種情況發生在經典的 Builder 模式派上用場。 讓我們看看如何在反序列化中使用它的力量。 首先,讓我們聲明一個全參數私有構造函數和一個Builder類:
private Person(String name, Integer age) {
this.name = name;
this.age = age;
}
static class Builder {
String name;
Integer age;
Builder withName(String name) {
this.name = name;
return this;
}
Builder withAge(Integer age) {
this.age = age;
return this;
}
public Person build() {
return new Person(name, age);
}
}
為了讓 Jackson 反序列化器使用該Builder,我們只需要在我們的代碼中添加兩個註解。 首先,我們需要用@JsonDeserialize註解標記我們的類,傳遞builder參數,其中包含構建器的完全限定域名。
之後,我們需要對構建器類本身進行註解,使其帶有@JsonPOJOBuilder:
@JsonDeserialize(builder = Person.Builder.class)
public class Person {
//...
@JsonPOJOBuilder
static class Builder {
//...
}
}
請注意,我們可以自定義用於構建過程中使用的方法的名稱。
參數buildMethodName默認值為“build”,代表我們調用構建器生成新對象時調用的方法名。
另一個參數,withPrefix,代表我們為負責設置屬性的構建器方法添加的前綴。 此參數的默認值為“with”。 這就是我們示例中未指定這些參數的原因。
以下是一個涵蓋Person對象反序列化的簡單單元測試:
String json = "{\"name\":\"Frank\",\"age\":50}";
Person person = new ObjectMapper().readValue(json, Person.class);
assertEquals("Frank", person.getName());
assertEquals(50, person.getAge().intValue());
5. 結論
在本文中,我們看到了如何使用 Jackson 庫反序列化不可變對象。