无法使用 Jackson 反序列化通用 class 层次结构
Cannot deserialize generic class hierarchy using Jackson
例如,我从外部供应商那里收到这个 JSON(其中 payload
可以是可变的):
{
"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"
}
我正在尝试使用此 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
}
...然后有这些可能的(通用)类型:
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 引导,因此我正在反序列化 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>> { }
}
...但最终因为我在这里发布了这个,Jackson 到目前为止都不喜欢这些。我尝试了几种方法,例如:JsonTypeInfo
和 JsonSubTypes
,但我无法更改 JSON 输入。
有人吗?有什么线索吗?
我们最终在 JSON 中添加了另一个 key/value 对——合约确实做了一些修改以适应 "private" key/value 对。
无论如何,如果有人遇到同样的问题,这是方法的解决方案:
...
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; }
}
}
例如,我从外部供应商那里收到这个 JSON(其中 payload
可以是可变的):
{
"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"
}
我正在尝试使用此 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
}
...然后有这些可能的(通用)类型:
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 引导,因此我正在反序列化 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>> { }
}
...但最终因为我在这里发布了这个,Jackson 到目前为止都不喜欢这些。我尝试了几种方法,例如:JsonTypeInfo
和 JsonSubTypes
,但我无法更改 JSON 输入。
有人吗?有什么线索吗?
我们最终在 JSON 中添加了另一个 key/value 对——合约确实做了一些修改以适应 "private" key/value 对。
无论如何,如果有人遇到同样的问题,这是方法的解决方案:
...
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; }
}
}