无法使用杰克逊反序列化通用类层次结构 [英] Cannot deserialize generic class hierarchy using Jackson

查看:87
本文介绍了无法使用杰克逊反序列化通用类层次结构的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

例如,我从外部供应商(其中payload可以是可变的)接收此JSON:

{
  "payload": {
    "enrolledAt": "2018-11-05T00:00:00-05:00",
    "userId": "99c7ff5c-2c4e-423f-abeb-2e5f3709a42a"
  },
  "requestId": "80517bb8-2a95-4f15-9a73-fcf3752a1147",
  "eventType": "event.success",
  "createdAt": "2018-11-05T16:55:13.762-05:00"
}

我正在尝试使用此类对它们进行建模:

public final class Notification<T extends AbstractModel> {
  @JsonProperty("requestId")
  private String requestId;

  @JsonProperty("eventType")
  private String eventType;

  @JsonProperty("createdAt")
  private ZonedDateTime createdAt;

  private T payload;

  @JsonCreator
  public Notification(@JsonProperty("payload") T payload) {
    requestId = UUID.randomUUID().toString();
    eventType = payload.getType();
    createdAt = ZonedDateTime.now();
    this.payload = payload;
  }

  // getters
}

...然后具有以下可能的(通用)类型:

public abstract class AbstractModel {
  private String userId;

  private Type type;

  @JsonCreator
  AbstractModel(@JsonProperty("companyUserId") String userId, @JsonProperty("type") Type type) {
    this.userId = userId;
    this.type = type;
  }

  // getters

  public enum Type {
    CANCEL("event.cancel"),
    SUCCESS("event.success");

    private final String value;

    Type(String value) {
      this.value = value;
    }

    public String getValue() { return value; }
  }
}


public final class Success extends AbstractModel {
  private ZonedDateTime enrolledAt;

  @JsonCreator
  public Success(String userId, @JsonProperty("enrolledAt") ZonedDateTime enrolledAt) {
    super(userId, Type.SUCCESS);
    this.enrolledAt = enrolledAt;
  }

  // getters
}


public final class Cancel extends AbstractModel {
  private ZonedDateTime cancelledAt;

  private String reason;

  @JsonCreator
  public Cancel(String userId, @JsonProperty("cancelledAt") ZonedDateTime cancelledAt,
      @JsonProperty("reason") String reason) {
    super(userId, Type.CANCEL);
    this.cancelledAt = cancelledAt;
    this.reason = reason;
  }

  // getters
}

该应用程序基于Spring Boot,因此我正在反序列化JSON,例如:

@Component
public final class NotificationMapper {    
  private ObjectMapper mapper;

  public NotificationMapper(final ObjectMapper mapper) {
    this.mapper = mapper;
  }

  public Optional<Notification<? extends AbstractModel>> deserializeFrom(final String thiz) {
    try {
      return Optional.of(mapper.readValue(thiz, new NotificationTypeReference()));
    } catch (final Exception e) { /* log errors here */ }
    return Optional.empty();
  }

  private static final class NotificationTypeReference extends TypeReference<Notification<? extends AbstractModel>> { }
}

...但是最终由于我将其发布在这里,杰克逊到目前为止还不喜欢任何一个.我已经尝试了诸如JsonTypeInfoJsonSubTypes的几种方法,但是我无法更改JSON输入.

有人吗?有任何线索吗?

解决方案

我们最终在JSON中添加了另一个键/值对–确实对合同进行了一些修改以适应该私有"键/值对. /p>

无论如何,如果有人遇到相同的问题,这是该方法的解决方案:

...
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

@JsonIgnoreProperties("_type")
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "_type")
@JsonSubTypes({
  @JsonSubTypes.Type(value = Cancel.class, name = "_type"),
  @JsonSubTypes.Type(value = Fail.class, name = "_type"),
  @JsonSubTypes.Type(value = Success.class, name = "_type"),
})
public abstract class AbstractModel {
  private String userId;

  private Type type;

  AbstractModel() { }

  AbstractModel(final String userId, final Type type) {
    this.userId = userId;
    this.type = type;
  }

  // getters, toString, etc.

  public enum Type {
    CANCEL("event.cancelled"),
    FAIL("event.failed"),
    SUCCESS("event.success");

    private final String value;

    Type(String value) {
      this.value = value;
    }

    public String getValue() { return value; }
  }
}

I'm receiving, for instance, this JSON from an external vendor (where payload can be variable):

{
  "payload": {
    "enrolledAt": "2018-11-05T00:00:00-05:00",
    "userId": "99c7ff5c-2c4e-423f-abeb-2e5f3709a42a"
  },
  "requestId": "80517bb8-2a95-4f15-9a73-fcf3752a1147",
  "eventType": "event.success",
  "createdAt": "2018-11-05T16:55:13.762-05:00"
}

I'm trying to model these using this class:

public final class Notification<T extends AbstractModel> {
  @JsonProperty("requestId")
  private String requestId;

  @JsonProperty("eventType")
  private String eventType;

  @JsonProperty("createdAt")
  private ZonedDateTime createdAt;

  private T payload;

  @JsonCreator
  public Notification(@JsonProperty("payload") T payload) {
    requestId = UUID.randomUUID().toString();
    eventType = payload.getType();
    createdAt = ZonedDateTime.now();
    this.payload = payload;
  }

  // getters
}

...and then having these possible (generic) types:

public abstract class AbstractModel {
  private String userId;

  private Type type;

  @JsonCreator
  AbstractModel(@JsonProperty("companyUserId") String userId, @JsonProperty("type") Type type) {
    this.userId = userId;
    this.type = type;
  }

  // getters

  public enum Type {
    CANCEL("event.cancel"),
    SUCCESS("event.success");

    private final String value;

    Type(String value) {
      this.value = value;
    }

    public String getValue() { return value; }
  }
}


public final class Success extends AbstractModel {
  private ZonedDateTime enrolledAt;

  @JsonCreator
  public Success(String userId, @JsonProperty("enrolledAt") ZonedDateTime enrolledAt) {
    super(userId, Type.SUCCESS);
    this.enrolledAt = enrolledAt;
  }

  // getters
}


public final class Cancel extends AbstractModel {
  private ZonedDateTime cancelledAt;

  private String reason;

  @JsonCreator
  public Cancel(String userId, @JsonProperty("cancelledAt") ZonedDateTime cancelledAt,
      @JsonProperty("reason") String reason) {
    super(userId, Type.CANCEL);
    this.cancelledAt = cancelledAt;
    this.reason = reason;
  }

  // getters
}

The application is based on Spring Boot, so I'm deserializing the JSON like:

@Component
public final class NotificationMapper {    
  private ObjectMapper mapper;

  public NotificationMapper(final ObjectMapper mapper) {
    this.mapper = mapper;
  }

  public Optional<Notification<? extends AbstractModel>> deserializeFrom(final String thiz) {
    try {
      return Optional.of(mapper.readValue(thiz, new NotificationTypeReference()));
    } catch (final Exception e) { /* log errors here */ }
    return Optional.empty();
  }

  private static final class NotificationTypeReference extends TypeReference<Notification<? extends AbstractModel>> { }
}

...but eventually since I'm posting this right here, Jackson doesn't like any of that so far. I've tried several things like: JsonTypeInfo and JsonSubTypes, but I can't change the JSON input.

Anyone? Any clue(s)?

解决方案

We ended up adding another key/value pair in the JSON – the contract was indeed modified a little bit to accommodate that "private" key/value pair.

In any case, if somebody runs into the same issue, this is the solution for the approach:

...
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

@JsonIgnoreProperties("_type")
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "_type")
@JsonSubTypes({
  @JsonSubTypes.Type(value = Cancel.class, name = "_type"),
  @JsonSubTypes.Type(value = Fail.class, name = "_type"),
  @JsonSubTypes.Type(value = Success.class, name = "_type"),
})
public abstract class AbstractModel {
  private String userId;

  private Type type;

  AbstractModel() { }

  AbstractModel(final String userId, final Type type) {
    this.userId = userId;
    this.type = type;
  }

  // getters, toString, etc.

  public enum Type {
    CANCEL("event.cancelled"),
    FAIL("event.failed"),
    SUCCESS("event.success");

    private final String value;

    Type(String value) {
      this.value = value;
    }

    public String getValue() { return value; }
  }
}

这篇关于无法使用杰克逊反序列化通用类层次结构的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆