java - Jackson 的多态反序列化问题

标签 java json jackson deserialization json-deserialization

我有以下类,我想使用 Jackson 将 JSON 字符串反序列化。

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
}

WebDeviceInfo.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 父类(super class),该父类(super class)没有端点字段?

我尝试在不同的类中使用 @JsonTypeInfo@JsonSubTypes 注释,但我找不到如何使用它们的好示例。

编辑:我将@JsonDeserialize(using = DeviceInfoDeserializer.class)注释添加到我的DeviceInfo类中,并创建了以下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.StackOverflowError: 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) 即可我的子类,现在它按预期工作。非常感谢@Luciano van der Veekens .

最佳答案

Jackson 不知道多态性,它只是尝试创建具体的 DeviceInfo 类的实例。

但是,您可以实现一个自定义反序列化器,以编程方式解析设备信息 JSON,并了解何时实例化某个子类,因为某些字段(例如 endpoint)具有唯一性。

https://fasterxml.github.io/jackson-databind/javadoc/2.2.0/com/fasterxml/jackson/databind/annotation/JsonDeserialize.html

@JsonDeserialize(using = DeviceInfoDeserializer.class)
public class DeviceInfo {
}

可以在此处找到示例:http://sunilkumarpblog.blogspot.nl/2015/12/javajson-polymorphic-serialization-de.html

关于java - Jackson 的多态反序列化问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44550662/

相关文章:

java - 准备语句的问题

java - 从另一个类访问字段的值

javax.servlet.ServletException : org. glassfish.jersey.server.ContainerException : java. lang.NoClassDefFoundError:org/xml/sax/helpers/DefaultHandler

java - 如何使用 JSON 按声明顺序序列化带有枚举键的映射?

java - 如何使用 jackson ObjectMapper 解析 Mongodb 的 UUID 对象?

java - 向多个收件人发送邮件

java - 流式传输到 BigQuery 表的数据何时可用于查询操作?

c# - 将对象列表以 json 格式保存到 blob

android - Android 的 JSON 解析 - 嵌套项目?

Java:在 JSON 对象中找到每个属性的确切路径