1. 簡介
Jackson 使用中,理解如何將 JSON 數據映射到 Java 對象至關重要,這通常涉及到使用構造函數。此外,<em>ConstructorDetector</em> 是 Jackson 中的關鍵組件,它會影響在反序列化過程中構造函數的選擇。
在本教程中,我們將詳細探討 ConstructorDetector,解釋其目的、配置和用法。
2. The Constructordetector: an Overview
ConstructorDetector 是 Jackson 的數據綁定模塊中的一項特性,它有助於確定在反序列化過程中應使用的類的構造函數。Jackson 使用構造函數來實例化對象並使用 JSON 數據填充其字段。
The ConstructorDetector allows us to customize and control which constructors Jackson should use, providing more flexibility in the deserialization process.
3. 配置 構造函數檢測器 (ConstructorDetector)
Jackson 提供了多種預定義的 構造函數檢測器 配置,包括 USE_PROPERTIES_BASED、USE_DELEGATING、EXPLICIT_ONLY 和 DEFAULT。
3.1. 使用基於屬性的配置
此配置在我們的類具有與 JSON 屬性相匹配的構造函數時非常有用。 讓我們以一個簡單的實際示例為例:
public class User {
private String firstName;
private String lastName;
private int age;
public User(){
}
public User(String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public int getAge() {
return age;
}
}在當前場景中,類 User 具有 firstName、lastName 和 age 屬性。Jackson 將查找類 User 中具有與這些屬性匹配的參數的構造函數,它在提供的 User(String firstName, String lastName, int age) 構造函數中找到了該構造函數。
現在,當使用 Jackson 將 JSON 對象反序列化為 Java 對象時,如果使用 ConstructorDetector.USE_PROPERTIES_BASED,Jackson 將利用與 JSON 對象屬性最匹配的構造函數:
@Test
public void givenUserJson_whenUsingPropertiesBased_thenCorrect() throws Exception {
String json = "{\"firstName\": \"John\", \"lastName\": \"Doe\", \"age\": 25}";
ObjectMapper mapper = JsonMapper.builder()
.constructorDetector(ConstructorDetector.USE_PROPERTIES_BASED)
.build();
User user = mapper.readValue(json, User.class);
assertEquals("John", user.getFirstName());
assertEquals(25, user.getAge());
}這裏,名為 json 的字符串代表一個具有屬性 firstName , lastName , 和 age 的 JSON 對象,這對應於 User 類的構造函數參數。 當使用 mapper 中的 Jackson 進行反序列化時,它將使用 readValue(json, User.class) 方法,以利用具有與 JSON 屬性匹配的參數的構造函數。
如果 JSON 包含不在類中定義的額外字段,Jackson 將忽略這些字段而不會拋出錯誤。例如:
String json = "{\"firstName\": \"John\", \"lastName\": \"Doe\", \"age\": 25, \"extraField\": \"extraValue\"}";
User user = mapper.readValue(json, User.class);在這種情況下,extraField會被忽略。但是,如果構造函數參數與JSON屬性不完全匹配,Jackson可能無法找到合適的構造函數並拋出錯誤。
3.2. 使用委託機制</h3
使用委託機制配置允許 Jackson 將對象創建委託給具有單參數構造函數的對象。這在 JSON 數據結構與單參數結構對齊時非常有效,可以實現簡潔的對象創建。
考慮一個用例,我們有一個名為 StringWrapper 的類,它封裝了一個字符串值:
public class StringWrapper {
private String value;
@JsonCreator(mode = JsonCreator.Mode.DELEGATING)
public StringWrapper(@JsonProperty("value") String value) {
this.value = value;
}
@JsonProperty("value")
public String getValue() {
return value;
}
}StringWrapper 類有一個單參數構造函數,帶有 @JsonCreator 和 @JsonProperty 註解,指示 Jackson 應該使用委託進行對象創建。
讓我們使用 Jackson 和 ConstructorDetector.USE_DELEGATING 將 JSON 反序列化為 Java 對象。
@Test
public void givenStringJson_whenUsingDelegating_thenCorrect() throws Exception {
String json = "\"Hello, world!\"";
ObjectMapper mapper = JsonMapper.builder()
.constructorDetector(ConstructorDetector.USE_DELEGATING)
.build();
StringWrapper wrapper = mapper.readValue(json, StringWrapper.class);
assertEquals("Hello, world!", wrapper.getValue());
}在這裏,我們使用 Jackson 及其 ConstructorDetector.USE_DELEGATING 選項,將 JSON 字符串值 “Hello, world!” 反序列化為 StringWrapper 對象。 Jackson 利用了 StringWrapper 的單參數構造函數,正確地映射了 JSON 字符串值。
如果 JSON 結構與單參數構造函數不匹配,Jackson 將會拋出錯誤。例如:
String json = "{\"value\": \"Hello, world!\", \"extraField\": \"extraValue\"}";
StringWrapper wrapper = mapper.readValue(json, StringWrapper.class);在這種情況下,額外的字段 extraField 導致錯誤,因為構造函數期望一個字符串值,而不是 JSON 對象。
3.3. 強制僅使用顯式構造函數
本配置確保僅使用明確標註的構造函數。此外,它還提供對構造函數選擇的嚴格控制,允許開發者在反序列化過程中指定 Jackson 應該考慮哪些構造函數。
請考慮以下場景,其中類 Product 代表一個具有 名稱 和 價格 的產品:
public class Product {
private String value;
private double price;
@JsonCreator
public Product(@JsonProperty("value") String value, @JsonProperty("price") double price) {
this.value = value;
this.price = price;
}
public String getName() {
return value;
}
public double getPrice() {
return price;
}
}此類有一個帶有 @JsonCreator 註解的構造函數,指示 Jackson 在反序列化過程中應使用基於構造函數的顯式實例化。
讓我們觀察反序列化過程:
@Test
public void givenProductJson_whenUsingExplicitOnly_thenCorrect() throws Exception {
String json = "{\"value\": \"Laptop\", \"price\": 999.99}";
ObjectMapper mapper = JsonMapper.builder()
.constructorDetector(ConstructorDetector.EXPLICIT_ONLY)
.build();
Product product = mapper.readValue(json, Product.class);
assertEquals("Laptop", product.getName());
assertEquals(999.99, product.getPrice(), 0.001);
}在本測試方法中,我們利用了 ConstructorDetector.EXPLICIT_ONLY 配置項,將一個表示產品的 JSON 對象反序列化為 Product 對象。 僅會考慮 Product 類中已定義的構造函數。
Jackson 在 JSON 對象包含構造函數中未定義的額外字段或缺少必需字段時,將會拋出錯誤。例如:
String json = "{\"value\": \"Laptop\"}";
Product product = mapper.readValue(json, Product.class);這將導致錯誤,因為 價格 字段缺失。
String json = "{\"value\": \"Laptop\", \"price\": 999.99, \"extraField\": \"extraValue\"}";
Product product = mapper.readValue(json, Product.class);這也會因為意外的字段 導致錯誤。
3.4. 默認配置
默認配置提供了一種平衡的方法,它考慮了構造函數選擇的各種策略。這種配置類型旨在選擇與 JSON 結構對齊的構造函數,同時考慮自定義註解和其他配置選項。
請考慮一個場景,其中 Address 類表示郵寄地址:
public class Address {
private String street;
private String city;
public Address(){
}
public Address(String street, String city) {
this.street = street;
this.city = city;
}
public String getStreet() {
return street;
}
public String getCity() {
return city;
}
}Address 類有一個帶有與 JSON 屬性street 和city 匹配參數的構造函數。
讓我們使用 Jackson 和ConstructorDetector.DEFAULT 將 JSON 反序列化為 Java 對象:
@Test
public void givenAddressJson_whenUsingDefault_thenCorrect() throws Exception {
String json = "{\"street\": \"123 Main St\", \"city\": \"Springfield\"}";
ObjectMapper mapper = JsonMapper.builder()
.constructorDetector(ConstructorDetector.DEFAULT)
.build();
Address address = mapper.readValue(json, Address.class);
assertEquals("123 Main St", address.getStreet());
assertEquals("Springfield", address.getCity());
}Jackson 採用默認啓發式算法選擇與 JSON 結構匹配的構造函數,從而確保從 JSON 數據中準確實例化對象。
如果 JSON 結構更復雜或包含不直接匹配任何構造函數的嵌套對象,Jackson 可能無法找到合適的構造函數並拋出錯誤。例如:
String json = "{\"street\": \"123 Main St\", \"city\": \"Springfield\", \"extraField\": \"extraValue\"}";
Address address = mapper.readValue(json, Address.class);在這種情況下,extraField 如果默認配置無法處理它,可能會導致錯誤。
4. 結論
綜上所述,理解 ConstructorDetector 在 Jackson 中的目的和配置對於在反序列化過程中將 JSON 數據有效映射到 Java 對象至關重要。