首页 > 解决方案 > 如何用不同的继承人反序列化 JSON

问题描述

json有几种类型,我举两个例子:

{
  "name": "dataUpdated",
  "version": 102,
  "eventTime": "2021-06-24",
  "payload": {
    "id": 158420,
    "name": 446970,
    "date": "2021-06-16",
    "type": "A"
}

{
  "name": "dataError",
  "version": 102,
  "eventTime": "2021-06-25",
  "payload": {
    "error": 666,
    "errorMsg": "forbidden"
}

只有有效载荷的结构发生变化

我有一个类 MessageDto

class MessageDto {
    
        @JsonProperty("name")
        String name
    
        @JsonProperty("version")
        Integer version
    
        @JsonProperty("eventTime")
        String eventTime
        
        @JsonProperty("payload")
        Payload payload 
    }

类 Payload 为空,我只是将其用作标记并从中继承两个类:

DataUpdatedDto extends Payload {

        @JsonProperty("id")
        BigInteger id

        @JsonProperty("name")
        String name

        @JsonProperty("date")
        String date
        
        @JsonProperty("type")
        String type

}

DataErrorDto extends Payload {
    
            @JsonProperty("error")
            Integer error
    
            @JsonProperty("errorMsg")
            String errorMsg
    }

如何向反序列化器解释我想反序列化 Payload 的哪个后代?

标签: javajsongroovyjackson

解决方案


您需要设置以下内容:

  • @JsonTypeInfo用于设置机制以应用某些策略来分派到某些实现 类
    • include=JsonTypeInfo.As.EXTERNAL_PROPERTY因为您要调度的内容不在有效载荷,而是在地图的同一级别
    • use=JsonTypeInfo.Id.NAME, property="name"name属性中读取值以进行调度
  • @JsonSubTypes@JsonSubTypes.Type设置 一起使用
    • 例如@JsonSubTypes.Type(value=DataUpdatedDto, name="dataUpdated") ,将类与父映射内部的DataUpdatedDto值 相关联dataUpdated

完整示例

@Grab('com.fasterxml.jackson.core:jackson-databind:2.12.4')
import com.fasterxml.jackson.annotation.*
import com.fasterxml.jackson.databind.*

class MessageDto {

    @JsonProperty("name")
    String name

    @JsonProperty("version")
    Integer version

    @JsonProperty("eventTime")
    String eventTime

    @JsonProperty("payload")
    @JsonTypeInfo(include=JsonTypeInfo.As.EXTERNAL_PROPERTY, use=JsonTypeInfo.Id.NAME, property="name")
    @JsonSubTypes([
            @JsonSubTypes.Type(value=DataUpdatedDto, name="dataUpdated"),
            @JsonSubTypes.Type(value=DataErrorDto, name="dataError")
    ])
    Payload payload 
}

interface Payload {}

class DataUpdatedDto implements Payload {

    @JsonProperty("id")
    BigInteger id

    @JsonProperty("name")
    String name

    @JsonProperty("date")
    String date

    @JsonProperty("type")
    String type

}

class DataErrorDto implements Payload {

    @JsonProperty("error")
    Integer error

    @JsonProperty("errorMsg")
    String errorMsg
}

def objectMapper = new ObjectMapper()
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)

def updated = objectMapper.readValue("""
{
  "name": "dataUpdated",
  "version": 102,
  "eventTime": "2021-06-24",
  "payload": {
    "id": 158420,
    "name": 446970,
    "date": "2021-06-16",
    "type": "A"
  }
}
""", MessageDto)

assert updated.payload instanceof DataUpdatedDto

def error = objectMapper.readValue("""
{
  "name": "dataError",
  "version": 102,
  "eventTime": "2021-06-25",
  "payload": {
    "error": 666,
    "errorMsg": "forbidden"
  }
}
""", MessageDto)

assert error.payload instanceof DataErrorDto

推荐阅读