當使用Jackson庫進行JSON到Java對象的反序列化時,其默認機制通常是首先調用類的無參構造函數實例化對象,然后通過字段的setter方法或直接訪問字段來填充數據。然而,這種默認機制在遇到帶有final修飾符的字段時會失效。
final字段的特性是它們只能被賦值一次,通常在聲明時、實例初始化塊中或構造函數中完成。一旦賦值,就不能再次修改。Jackson的默認反序列化流程試圖在對象實例化后通過setter(或直接字段注入)來設置這些final字段,這與final的特性相悖,從而導致MismatchedInputException,錯誤信息通常會提示“Cannot construct instance of ... (no delegate- or property-based Creator)”。
例如,對于以下User類:
@Data public final class User implements Serializable { @JsonProperty("alias") private final String alias; }
由于alias字段是final的,Jackson無法在實例化User對象后再通過setter設置其值,因此會拋出反序列化異常。要解決這個問題,需要明確告訴Jackson如何通過構造函數來創(chuàng)建和初始化對象。
最直接且明確的解決方案是為帶有final字段的類提供一個參數化構造函數,并使用@JsonCreator注解標記它,同時使用@JsonProperty注解指定構造函數參數與JSON屬性的映射關系。
示例:
@Data public final class User implements Serializable { @JsonProperty("alias") private final String alias; @JsonCreator public User(@JsonProperty("alias") String alias){ this.alias = alias; } }
在這個例子中:
這種方法清晰明了,適用于所有情況,包括單參數和多參數構造函數。
另一種更靈活的解決方案是利用Jackson的ParameterNamesModule。這個模塊允許Jackson在Java 8及更高版本中,通過JVM的參數名稱發(fā)現(xiàn)功能(需要編譯時開啟-parameters選項),自動推斷構造函數參數的名稱,從而無需在每個參數上顯式添加@JsonProperty注解。
首先,需要在項目的pom.xml(Maven)或build.gradle(Gradle)中添加jackson-modules-java8依賴,它包含了ParameterNamesModule:
<dependency> <groupId>com.fasterxml.jackson.module</groupId> <artifactId>jackson-modules-java8</artifactId> <version>2.13.3</version> <!-- 請使用最新穩(wěn)定版本 --> </dependency>
接下來,需要將ParameterNamesModule注冊到Jackson的ObjectMapper實例中。在Spring Boot應用中,可以通過將ParameterNamesModule聲明為一個Spring Bean來自動配置ObjectMapper:
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class JacksonConfig { @Bean public ParameterNamesModule parameterNamesModule() { // JsonCreator.Mode.PROPERTIES 模式表示參數將從JSON對象的屬性中綁定 return new ParameterNamesModule(JsonCreator.Mode.PROPERTIES); } }
通過這種配置,Jackson在反序列化時,如果發(fā)現(xiàn)類有參數化構造函數,并且字段是final的,它會嘗試使用該構造函數,并根據參數名(通過ParameterNamesModule獲?。┢ヅ銳SON屬性。
盡管ParameterNamesModule非常方便,但它有一個重要的“陷阱”:對于只有一個參數的構造函數,即使啟用了ParameterNamesModule,其參數仍需要顯式地使用@JsonProperty注解。這是為了保持與舊版本Jackson行為的兼容性。
引用官方文檔的解釋:
這意味著,如果你的User類構造函數只有一個參數:
@Data public final class User implements Serializable { @JsonProperty("alias") private final String alias; // 即使配置了ParameterNamesModule,這個單參數構造函數也需要@JsonProperty public User(String alias){ // 錯誤示范,缺少@JsonProperty this.alias = alias; } // 正確示范: @JsonCreator // 可選,但明確更好 public User(@JsonProperty("alias") String alias){ this.alias = alias; } }
而對于多參數構造函數,例如Multiplication類:
@Data public final class Multiplication implements Serializable { @JsonProperty("factorA") private final Integer factorA; @JsonProperty("factorB") private final Integer factorB; // 假設存在以下構造函數,且ParameterNamesModule已配置 public Multiplication(Integer factorA, Integer factorB) { this.factorA = factorA; this.factorB = factorB; } }
如果Multiplication類具有如上所示的多參數構造函數,并且ParameterNamesModule已正確配置且項目編譯時開啟了-parameters選項,那么Jackson在反序列化時可以自動識別并使用這個構造函數,而無需在factorA和factorB參數上顯式添加@JsonProperty(當然,添加了也不會有問題)。這解釋了為什么在某些情況下,帶有final字段的多參數對象可能無需@JsonCreator也能成功反序列化。
理解這些機制有助于開發(fā)者更有效地使用Jackson進行JSON處理,尤其是在構建不可變對象或使用Lombok的@Data配合final字段時。
以上就是Jackson反序列化中@JsonCreator方法的使用時機與策略的詳細內容,更多請關注php中文網其它相關文章!
每個人都需要一臺速度更快、更穩(wěn)定的 PC。隨著時間的推移,垃圾文件、舊注冊表數據和不必要的后臺進程會占用資源并降低性能。幸運的是,許多工具可以讓 Windows 保持平穩(wěn)運行。
Copyright 2014-2025 http://www.miracleart.cn/ All Rights Reserved | php.cn | 湘ICP備2023035733號