当前位置: 首页 > 知识库问答 >
问题:

Jackson的多态反序列化问题

公良俊楚
2023-03-14

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

PushNotificationMessage.java

public class PushNotificationMessage {
    @JsonProperty("device_info")
    private DeviceInfo deviceInfo;
    private String content;

    //getters & setters
}

设备信息。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.无法识别的字段endpoint(类com.blah.DeviceInfo),未标记为可忽略(一个已知属性:device_type])

如何让Jackson认识到它需要映射到WebDeviceInfo实例,而不是尝试将其映射到没有endpoint字段的DeviceInfo超类?

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

编辑:我将注释添加到我的设备信息类中,并创建了以下设备信息反序列化器。

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)时遇到了另一个错误:

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)

编辑:只需要将@jsondeseligate(as=WebDeviceInfo.class)@jsondeseligate(as=IOSDeviceInfo.class)添加到我的子类中,现在它可以正常工作了。非常感谢@Luciano van der Veekens。


共有1个答案

璩浩广
2023-03-14

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

 类似资料:
  • 我对Jackson和类型层次结构有以下问题。我正在序列化一个类SubA,该类将扩展为一个字符串,然后尝试将其反序列化回来。当然,在编译时,系统不知道它是基还是SubA,因此我希望它是基,如果它是SubA,则会在编译后执行一些其他操作。 我的基本类看起来像: ...和一个派生自的类: ... 我试图执行以下代码: String是: 但我一次又一次地犯同样的错误。映射器现在知道如何处理另一个类参数-它

  • 我想为我们的REST API实现一个自定义的反序列化器,它不仅被Java应用程序使用。因此,我不想让Jackson将类型信息放入序列化的JSON中。 我目前正在努力反序列化<code>CollectionExpand</code>,因为它包含特定<code>ResourceModel</code>的<code>数据</code>列表。 < code>ResourceModel是一个接口,每个< c

  • 我看过jackson反序列化@JsonTypeInfo的一个例子,那就是: 我试过了,效果很好。现在的问题是,在示例类中,Cat和Dog是从Animal中引用的,我想避免这种情况。有没有一种方法可以将类型绑定从类动物中移除,并且仍然进行反序列化工作?谢谢

  • 问题内容: 如果我有这样的类结构: 还有另一种反序列化的方法吗?在父类上使用此注释: 我不想强迫我的API的客户包括反序列化一个子类。 杰克逊不是使用,而是提供了一种方法来注释子类并通过唯一属性将其与其他子类区分开来?在上面的示例中,这类似于“如果JSON对象将其反序列化为,如果它将其反序列化为”。 问题答案: 感觉像是应该使用的东西,但是我在文档中进行了选择,可以提供的任何属性似乎都不符合您所描

  • 我想反序列化以下XML(缩短示例): 到目前为止,我想出的代码: 但这种方法存在几个问题: > @JacksonXmlProperty t(localName="subject")始终为空,因为我将其用于类型信息。为什么?,或者如何绕过它? 还是这种方法已经错了? 最后是我使用的周围类:

  • I'va是一个OID接口,可以由许多具体类型实现: 现在我有一个具有两个字段的对象,一个使用抽象接口类型(OID)定义,另一个使用具体类型(MyOID)定义 我想使用jackson以不同的方式序列化/反序列化字段,无论它们是使用抽象接口类型还是具体类型定义的: 注意,被序列化,包括类型信息(多态序列化),而被序列化为文本 为此,我将OID接口注释为: 并为每个具体类型分配了类型id: 最后,对容器