如何用不同的继承人反序列化 JSON

How to deserialize JSON with different heirs

有几种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"
}

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

我有一个 class MessageDto

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

Class Payload 是空的,我只是用它作为标记,并从中继承了两个class:

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 的哪个后代?

您需要设置以下内容:

  • 使用@JsonTypeInfo设置应用某些策略的机制 派遣到某些实施 classes
    • include=JsonTypeInfo.As.EXTERNAL_PROPERTY因为你想要的 dispatch 不是 inside payload,而是在同一层 地图
    • use=JsonTypeInfo.Id.NAME, property="name" 从中读取值 name 属性 进行调度
  • 使用 @JsonSubTypes@JsonSubTypes.Type 设置
    • 例如@JsonSubTypes.Type(value=DataUpdatedDto, name="dataUpdated") 将 class 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