动态类型绑定

参考链接:https://www.icode9.com/content-1-198969.html

基类:

  1. import com.fasterxml.jackson.annotation.JsonSubTypes;
  2. import com.fasterxml.jackson.annotation.JsonTypeInfo;
  3. import com.transform.module.deviceserver.snapshot.*;
  4. @JsonTypeInfo(property = "deviceType", use = JsonTypeInfo.Id.NAME)
  5. @JsonSubTypes({
  6. @JsonSubTypes.Type(value = LedSnapshot.class, name = "led"),
  7. @JsonSubTypes.Type(value = TvSnapshot.class, name = "tv")
  8. })
  9. public class BaseSnapshot {
  10. }

子类:

  1. public class LedSnapshot extends BaseSnapshot {
  2. private boolean power;
  3. private int mode;
  4. public boolean getPower() {
  5. return power;
  6. }
  7. public void setPower(boolean power) {
  8. this.power = power;
  9. }
  10. public int getMode() {
  11. return mode;
  12. }
  13. public void setMode(int mode) {
  14. this.mode = mode;
  15. }
  16. }

支持JDK8日期时间类型

添加依赖:

  1. <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.datatype/jackson-datatype-jsr310 -->
  2. <dependency>
  3. <groupId>com.fasterxml.jackson.datatype</groupId>
  4. <artifactId>jackson-datatype-jsr310</artifactId>
  5. <version>2.10.0</version>
  6. </dependency>

加载模块:

ObjectMapper mapper = new ObjectMapper();
// 只加载JDK8时间模块
// mapper.registerModule(new JavaTimeModule());
// 自动发现并加载所有模块
mapper.findAndRegisterModules();
// 调用方法进行解析,例如下一行
// Config config = mapper.readValue(builder.toString(), Config.class);

对内层节点进行解析及绑定

格式化后的配置文件:

{
    "test": {
      "num1": 123,
    "num2": 456
  }
}

数据类:

public class TestData {
    private int num1;
    private int num2;

    public int getNum1() {
        return num1;
    }

    public void setNum1(int num1) {
        this.num1 = num1;
    }

    public int getNum2() {
        return num2;
    }

    public void setNum2(int num2) {
        this.num2 = num2;
    }
}

绑定代码:

String content = "{\"test\": {\"num1\": 123, \"num2\": 456}}";
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(content);
// 读取内层节点
node = node.get("test");
// 数据绑定
TestData data = mapper.treeToValue(node, TestData.class);

YAML支持

添加依赖:

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml -->
<dependency>
  <groupId>com.fasterxml.jackson.dataformat</groupId>
  <artifactId>jackson-dataformat-yaml</artifactId>
  <version>2.10.0</version>
</dependency>

示例代码:

String content = "";
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
Config config = mapper.readValue(content, Config.class);

在反序列化中支持泛型

场景:HTTP响应的时候经常会在最顶层放一个通用的数据结构,然后其中某一项是业务数据。我们需要用一种通用且简洁的方法把数据反序列化出来,并且反序列化出的类型还需要方便使用。

数据结构

外层的通用结构

public class ResponseVo<T> {
    private T data;

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

内层的业务结构

public class Device {
    private String deviceId;

    public String getDeviceId() {
        return deviceId;
    }

    public void setDeviceId(String deviceId) {
        this.deviceId = deviceId;
    }
}

反序列化方法一:使用TypeReference

思路是实现TypeReference接口,将泛型类型放在TypeReference的泛型中。然后实例化该类的一个对象,把这个对象传入readValue方法中。

Tip: TypeReference实现类对象可以做成单例,避免每次反序列化都生成一次。

public class JacksonTest {
    private ObjectMapper objectMapper;

    public JacksonTest() {
        this.objectMapper = new ObjectMapper();
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    @Test
    public void first() throws JsonProcessingException {
        String json = "{\"data\": {\"deviceId\": \"132456\"}}";

        ResponseVo<Device> vo = objectMapper.readValue(json, 
                new TypeReference<ResponseVo<Device>>(){});
        System.out.println(objectMapper.writeValueAsString(vo));
    }
}

反序列化方法二:使用TypeFactory生成JavaType

思路是使用TypeFactory的方法构造一个描述类型的JavaType对象,然后把JavaType对象传入readValue中。

Tip: JavaType对象也可以做成单例避免每次生成。

public class JacksonTest {
    private ObjectMapper objectMapper;

    public JacksonTest() {
        this.objectMapper = new ObjectMapper();
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    @Test
    public void second() throws JsonProcessingException {
        String json = "{\"data\": {\"deviceId\": \"132456\"}}";

        ResponseVo<Device> vo = objectMapper.readValue(json,
                new TypeReference<ResponseVo<Device>>(){});
        System.out.println(objectMapper.writeValueAsString(vo));

        TypeFactory factory = objectMapper.getTypeFactory();
        ResponseVo<Device> vo = objectMapper.readValue(json, factory.constructParametricType(ResponseVo.class, Device.class));
        System.out.println(objectMapper.writeValueAsString(vo));
    }
}

不过TypeFactory使用起来不是那么直观易懂,而且方法注释也很不清晰。下面是我对方法进行测试后的一些猜测。

  • constructArrayType:用于Java数组类型
  • constructCollectionType:用于Collection子类
  • constructMapType:用于Map类型
  • constructParametricType:用于普通类的泛型
  • constructGeneralizedType:用于有父类关系的(不太懂)
  • constructSpecializedType:用于有子类关系的(不太懂)