Jackson 的多态反序列化问题
Polymorphic Deserialization Issue with Jackson
我有以下 classes,我想反序列化 JSON 字符串以使用 Jackson。
PushNotificationMessage.java
public class PushNotificationMessage {
@JsonProperty("device_info")
private DeviceInfo deviceInfo;
private String content;
//getters & setters
}
DeviceInfo.java
public class DeviceInfo {
@JsonProperty(value = "device_type")
private String deviceType;
//getters & setters
}
IOSDeviceInfo.java
public class IOSDeviceInfo extends DeviceInfo {
@JsonProperty(value = "device_id")
private String deviceId;
private String arn;
@JsonProperty(value = "user_data")
private String userData;
//getters & setters
}
网络DeviceInfo.java
public class WebDeviceInfo extends DeviceInfo {
private String endpoint;
private String key;
private String auth;
//getters & setters
}
我有以下 JSON 内容要反序列化:
{
"device_info": {
"endpoint": "https://android.googleapis.com/gcm/send/blah",
"key": "blahkey",
"auth": "blahauth",
"device_type": "web"
},
"content": "Notification content"
}
我只是使用 ObjectMapper
来尝试执行反序列化。
final ObjectMapper objectMapper = new ObjectMapper();
final PushNotificationMessage message = objectMapper.readValue(jsonString, PushNotifictionMessage.class);
当我这样做时,我得到:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "endpoint" (class com.blah.DeviceInfo), not marked as ignorable (one known property: "device_type"])
我怎样才能让 Jackson 认识到它需要被映射到一个 WebDeviceInfo
实例,而不是试图将它映射到 DeviceInfo
superclass,它不有 endpoint
字段?
我试过在不同的 class 中使用 @JsonTypeInfo
和 @JsonSubTypes
注释,但我找不到如何使用它们的好例子。
编辑: 我将 @JsonDeserialize(using = DeviceInfoDeserializer.class)
注释添加到我的 DeviceInfo
class,并创建了以下 DeviceInfoDeserializer
。
public class DeviceInfoDeserializer extends JsonDeserializer<DeviceInfo> {
private static final String DEVICE_TYPE = "device_type";
private static final String WEB = "web";
private static final String IOS = "ios";
@Override
public DeviceInfo deserialize(final JsonParser jsonParser, final DeserializationContext deserializationContext) throws IOException {
final ObjectMapper objectMapper = (ObjectMapper) jsonParser.getCodec();
final ObjectNode root = objectMapper.readTree(jsonParser);
if (root.has(DEVICE_TYPE)) {
final JsonNode jsonNode = root.get(DEVICE_TYPE);
if (jsonNode.asText().equalsIgnoreCase(WEB)) {
return objectMapper.readValue(root.toString(), WebDeviceInfo.class);
} else if (jsonNode.asText().equalsIgnoreCase(IOS)) {
return objectMapper.readValue(root.toString(), IOSDeviceInfo.class);
}
}
throw deserializationContext.mappingException("Failed to de-serialize device info, as device_type was not \"web\" or \"ios\"");
}
}
现在,我在尝试反序列化我的 PushNotificationMessage
JSON:
时遇到了不同的错误
java.lang.WhosebugError: null
at com.fasterxml.jackson.databind.deser.std.BaseNodeDeserializer.deserializeObject(JsonNodeDeserializer.java:210)
at com.fasterxml.jackson.databind.deser.std.JsonNodeDeserializer.deserialize(JsonNodeDeserializer.java:69)
at com.fasterxml.jackson.databind.deser.std.JsonNodeDeserializer.deserialize(JsonNodeDeserializer.java:15)
at com.fasterxml.jackson.databind.ObjectMapper._readValue(ObjectMapper.java:3770)
at com.fasterxml.jackson.databind.ObjectMapper.readTree(ObjectMapper.java:2207)
at com.blah.serialization.DeviceInfoDeserializer.deserialize(DeviceInfoDeserializer.java:25)
at com.blah.serialization.DeviceInfoDeserializer.deserialize(DeviceInfoDeserializer.java:16)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3798)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2842)
... (above trace repeated many times)
编辑: 只需将 @JsonDeserialize(as = WebDeviceInfo.class)
和 @JsonDeserialize(as = IOSDeviceInfo.class)
添加到我的子classes,现在它按预期工作了。非常感谢 。
Jackson 不了解多态性,它只是尝试创建具体 DeviceInfo 的实例 class。
但是,您可以实现自定义解串器,以编程方式解析设备信息 JSON 并知道何时实例化其中一个子 class,因为某些字段的唯一性,例如 endpoint
.
@JsonDeserialize(using = DeviceInfoDeserializer.class)
public class DeviceInfo {
}
可在此处找到示例:http://sunilkumarpblog.blogspot.nl/2015/12/javajson-polymorphic-serialization-de.html
我有以下 classes,我想反序列化 JSON 字符串以使用 Jackson。
PushNotificationMessage.java
public class PushNotificationMessage {
@JsonProperty("device_info")
private DeviceInfo deviceInfo;
private String content;
//getters & setters
}
DeviceInfo.java
public class DeviceInfo {
@JsonProperty(value = "device_type")
private String deviceType;
//getters & setters
}
IOSDeviceInfo.java
public class IOSDeviceInfo extends DeviceInfo {
@JsonProperty(value = "device_id")
private String deviceId;
private String arn;
@JsonProperty(value = "user_data")
private String userData;
//getters & setters
}
网络DeviceInfo.java
public class WebDeviceInfo extends DeviceInfo {
private String endpoint;
private String key;
private String auth;
//getters & setters
}
我有以下 JSON 内容要反序列化:
{
"device_info": {
"endpoint": "https://android.googleapis.com/gcm/send/blah",
"key": "blahkey",
"auth": "blahauth",
"device_type": "web"
},
"content": "Notification content"
}
我只是使用 ObjectMapper
来尝试执行反序列化。
final ObjectMapper objectMapper = new ObjectMapper();
final PushNotificationMessage message = objectMapper.readValue(jsonString, PushNotifictionMessage.class);
当我这样做时,我得到:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "endpoint" (class com.blah.DeviceInfo), not marked as ignorable (one known property: "device_type"])
我怎样才能让 Jackson 认识到它需要被映射到一个 WebDeviceInfo
实例,而不是试图将它映射到 DeviceInfo
superclass,它不有 endpoint
字段?
我试过在不同的 class 中使用 @JsonTypeInfo
和 @JsonSubTypes
注释,但我找不到如何使用它们的好例子。
编辑: 我将 @JsonDeserialize(using = DeviceInfoDeserializer.class)
注释添加到我的 DeviceInfo
class,并创建了以下 DeviceInfoDeserializer
。
public class DeviceInfoDeserializer extends JsonDeserializer<DeviceInfo> {
private static final String DEVICE_TYPE = "device_type";
private static final String WEB = "web";
private static final String IOS = "ios";
@Override
public DeviceInfo deserialize(final JsonParser jsonParser, final DeserializationContext deserializationContext) throws IOException {
final ObjectMapper objectMapper = (ObjectMapper) jsonParser.getCodec();
final ObjectNode root = objectMapper.readTree(jsonParser);
if (root.has(DEVICE_TYPE)) {
final JsonNode jsonNode = root.get(DEVICE_TYPE);
if (jsonNode.asText().equalsIgnoreCase(WEB)) {
return objectMapper.readValue(root.toString(), WebDeviceInfo.class);
} else if (jsonNode.asText().equalsIgnoreCase(IOS)) {
return objectMapper.readValue(root.toString(), IOSDeviceInfo.class);
}
}
throw deserializationContext.mappingException("Failed to de-serialize device info, as device_type was not \"web\" or \"ios\"");
}
}
现在,我在尝试反序列化我的 PushNotificationMessage
JSON:
java.lang.WhosebugError: null
at com.fasterxml.jackson.databind.deser.std.BaseNodeDeserializer.deserializeObject(JsonNodeDeserializer.java:210)
at com.fasterxml.jackson.databind.deser.std.JsonNodeDeserializer.deserialize(JsonNodeDeserializer.java:69)
at com.fasterxml.jackson.databind.deser.std.JsonNodeDeserializer.deserialize(JsonNodeDeserializer.java:15)
at com.fasterxml.jackson.databind.ObjectMapper._readValue(ObjectMapper.java:3770)
at com.fasterxml.jackson.databind.ObjectMapper.readTree(ObjectMapper.java:2207)
at com.blah.serialization.DeviceInfoDeserializer.deserialize(DeviceInfoDeserializer.java:25)
at com.blah.serialization.DeviceInfoDeserializer.deserialize(DeviceInfoDeserializer.java:16)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3798)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2842)
... (above trace repeated many times)
编辑: 只需将 @JsonDeserialize(as = WebDeviceInfo.class)
和 @JsonDeserialize(as = IOSDeviceInfo.class)
添加到我的子classes,现在它按预期工作了。非常感谢
Jackson 不了解多态性,它只是尝试创建具体 DeviceInfo 的实例 class。
但是,您可以实现自定义解串器,以编程方式解析设备信息 JSON 并知道何时实例化其中一个子 class,因为某些字段的唯一性,例如 endpoint
.
@JsonDeserialize(using = DeviceInfoDeserializer.class)
public class DeviceInfo {
}
可在此处找到示例:http://sunilkumarpblog.blogspot.nl/2015/12/javajson-polymorphic-serialization-de.html