Jackson反序列化不可变类

Jackson默认的反序列化策略需要无参构造器,并提供字段setter函数。

如下ImmutableUser类属性都被final修饰,只有全参构造器,没有setter方法,它的实例一经创建就不可变。如何使用Jackson反序列化它呢?

java 复制代码
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.

java 复制代码
@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更改反序列化策略。

java 复制代码
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-namesModule,正好可以实现无侵入的反序列化不可变类。引入依赖:

xml 复制代码
 <dependency>
      <groupId>com.fasterxml.jackson.module</groupId>
      <artifactId>jackson-module-parameter-names</artifactId>
      <version>${jackson.version}</version>
</dependency>

向ObjectMapper注册ParameterNamesModule,就可以执行成功了。

java 复制代码
@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字段。

java 复制代码
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

java 复制代码
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的构造器声明来绑定属性值。

java 复制代码
@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));
}

可以成功反序列化

相关推荐
bobz965几秒前
ovs patch port 对比 veth pair
后端
Asthenia041210 分钟前
Java受检异常与非受检异常分析
后端
uhakadotcom24 分钟前
快速开始使用 n8n
后端·面试·github
JavaGuide31 分钟前
公司来的新人用字符串存储日期,被组长怒怼了...
后端·mysql
bobz96541 分钟前
qemu 网络使用基础
后端
Asthenia04121 小时前
面试攻略:如何应对 Spring 启动流程的层层追问
后端
Asthenia04121 小时前
Spring 启动流程:比喻表达
后端
Asthenia04122 小时前
Spring 启动流程分析-含时序图
后端
ONE_Gua2 小时前
chromium魔改——CDP(Chrome DevTools Protocol)检测01
前端·后端·爬虫
致心2 小时前
记一次debian安装mariadb(带有迁移数据)
后端