Jackson預設的反序列化策略需要無參構造器,並提供欄位setter函式。
如下ImmutableUser類屬性都被final修飾,只有全參構造器,沒有setter方法,它的例項一經建立就不可變。如何使用Jackson反序列化它呢?
public class ImmutableUser { private final String name; private final int age; private final String identityCard; public ImmutableUser(String name, int age, String identityCard) { this.name = name; this.age = age; this.identityCard = identityCard; } public String getName() { return name; } public int getAge() { return age; } public String getIdentityCard() { return identityCard; } }
或許你會寫下這樣的程式碼,結果發現一執行就報錯了:no Creators, like default constructor, exist.
@Test public void readImmutableUser() throws JsonProcessingException { String userJson = "{"name":"Tom","age":23,"identityCard":"61012420000101012x"}"; ObjectMapper mapper = new ObjectMapper(); ImmutableUser user = mapper.readValue(userJson, ImmutableUser.class); System.out.println(MAPPER.writeValueAsString(user)); }
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of
com.learn.more.entiry.ImmutableUser
(no Creators, like default constructor, exist): ......
一 使用Jackson註解
可以使用@JsonProperty
更改反序列化策略。
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; // 與ImmutableUser的區別:使用@JsonProperty、@JsonCreator修飾全參構造器 public class ImmutableUser2 { private final String name; private final int age; private final String identityCard; @JsonCreator public ImmutableUser2(@JsonProperty("name") String name, @JsonProperty("age") int age, @JsonProperty("identityCard") String identityCard) { this.name = name; this.age = age; this.identityCard = identityCard; } public String getName() { return name; } public int getAge() { return age; } public String getIdentityCard() { return identityCard; } }
還是上面的測試程式碼,這次發現執行成功了。
需注意,@JsonProperty
的value必須填寫,且最好與Json串中欄位名一致。否則將導致下面的異常。
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Invalid type definition for type
com.learn.more.entiry.ImmutableUser2
: ..., annotations: ... has no property name (and is not Injectable): can not use as property-based Creator
二 使用jackson-module-parameter-names
如果ImmutableUser類來自jar包,我們無法修改原始碼來新增@JsonProperty、@JsonCreator註解。又該如何處理呢?
由官方維護的jackson-module-parameter-names
Module,正好可以實現無侵入的反序列化不可變類。引入依賴:
<dependency> <groupId>com.fasterxml.jackson.module</groupId> <artifactId>jackson-module-parameter-names</artifactId> <version>${jackson.version}</version> </dependency>
向ObjectMapper註冊ParameterNamesModule
,就可以執行成功了。
@Test public void readImmutableUser() throws JsonProcessingException { String userJson = "{"name":"Tom","age":23,"identityCard":"61012420000101012x"}"; ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new ParameterNamesModule()); ImmutableUser user = mapper.readValue(userJson, ImmutableUser.class); System.out.println(MAPPER.writeValueAsString(user)); }
三 使用Mixins機制
Jackson提供了mixins 機制,支援外掛式的序列化/反序列化策略宣告,從而避免對源數據結構的侵入性改變。我們反序列化第三方的不可變類時,可以使用該機制:
建立ImmutableUserMixin類,具有與ImmutableUser相似的構造器引數,使用 @JsonProperty宣告了引數對應的json欄位。
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; public class ImmutableUserMixin { @JsonCreator public ImmutableUserMixin(@JsonProperty("name") String name, @JsonProperty("age") int age, @JsonProperty("identityCard") String identityCard) { } }
再建立一個自定義的JacksonMixinModule
類,將ImmutableUser與ImmutableUserMixin對應關係,設定到SetupContext
。
import com.fasterxml.jackson.databind.module.SimpleModule; import com.learn.more.entiry.ImmutableUser; import com.learn.more.entiry.ImmutableUserMixin; public class JacksonMixinModule extends SimpleModule { public JacksonMixinModule() { super(JacksonMixinModule.class.getName()); } // 註冊所有使用Mixin機制的類 @Override public void setupModule(SetupContext context) { context.setMixInAnnotations(ImmutableUser.class, ImmutableUserMixin.class); // ...... } }
向ObjectMapper註冊JacksonMixinModule類。那麼,在反序列化ImmutableUser時,將依據ImmutableUserMixin的構造器宣告來繫結屬性值。
@Test public void readImmutableUserMixin() throws JsonProcessingException { String userJson = "{"name":"Tom","age":23,"identityCard":"61012420000101012x"}"; ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JacksonMixinModule()); ImmutableUser user = mapper.readValue(userJson, ImmutableUser.class); System.out.println(MAPPER.writeValueAsString(user)); }
可以成功反序列化