【问题标题】:Polymorphic deserialization in Jackson without annotationsJackson中的多态反序列化没有注释
【发布时间】:2019-08-10 02:51:33
【问题描述】:

我有一个 CloudEvent<T> 类,它使用 Jackson(2.9.0 - 最新版本)进行多态反序列化,如下所示:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class CloudEvent<T> {

    @NonNull
    private String eventType;

    @JsonTypeInfo(
            use = JsonTypeInfo.Id.NAME,
            include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
            property = "eventType",
            defaultImpl = Void.class)
    @JsonSubTypes({
            @JsonSubTypes.Type(value = MyEvent1.class, name = "event-1"),
            @JsonSubTypes.Type(value = MyEvent2.class, name = "event-2")
    })
    private T data;
}

然后反序列化为:

String cloudEventJson1 = "{\"eventType\":\"event-1\",\"data\":{\"id\":\"123\",\"details\":\"detail1\"}}";

CloudEvent deserializedEvent1 = objectMapper.readValue(cloudEventJson1, CloudEvent.class);   //without subtypes

这一切都很好。 但是由于一些限制,我不能在 CloudEvent 类上使用注解(由外部依赖提供)。

所以我这样配置 ObjectMapper:

    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.registerSubtypes(new NamedType(MyEvent1.class, "event-1"));
    objectMapper.registerSubtypes(new NamedType(MyEvent2.class, "event-2"));

    TypeResolverBuilder  typeResolverBuilder = new ObjectMapper.DefaultTypeResolverBuilder(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE)
      .init(JsonTypeInfo.Id.NAME, null)   //CLASS works
      .inclusion(JsonTypeInfo.As.EXTERNAL_PROPERTY)
      .typeProperty("eventType")
      .typeIdVisibility(true)
//    .defaultImpl(Void.class);
    objectMapper.setDefaultTyping(typeResolverBuilder);

但是用与上面相同的方法反序列化不起作用。它正在读取eventType,但无法匹配已注册的子类型。 我不能在反序列化中使用泛型或 TypeReference,因为我需要使用spring-integration 来读取只接受主类的事件;模式匹配在反序列化后手动完成。

例外:

com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve type id 'event-1' as a subtype of [simple type, class java.lang.Object]: known type ids = [] (for POJO property 'data')
 at [Source: (String)"{"eventType":"event-1","data":{"id":"123","details":"detail1"}}"; line: 1, column: 271]

这也是为所有输入类配置 ObjectMapper。是否可以将此typeResolverBuildersubtypes 连接到CloudEvent.class(就像注释方式一样)。

【问题讨论】:

    标签: java json spring jackson deserialization


    【解决方案1】:

    即使你不能修改你的类,你仍然可以依赖注解。 Jackson 支持一种称为 mix-ins 的功能:您可以将其视为一种在运行时添加更多注释的面向方面的方式,以增加静态定义的注释。

    首先定义一个接口如下:

    public interface CloudEventMixIn<T> {
    
         @JsonTypeInfo(
                use = JsonTypeInfo.Id.NAME,
                include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
                property = "eventType",
                defaultImpl = Void.class)
        @JsonSubTypes({
                @JsonSubTypes.Type(value = MyEvent1.class, name = "event-1"),
                @JsonSubTypes.Type(value = MyEvent2.class, name = "event-2")
        })
        public T getData();
    }
    

    然后配置ObjectMapper 使用定义的接口作为实际类/接口的混入:

    ObjectMapper mapper = new ObjectMapper();
    mapper.addMixIn(CloudEvent.class, CloudEventMixIn.class);
    

    来自addMixIn(Class&lt;?&gt; target, Class&lt;?&gt; mixinSource) 方法文档:

    用于添加混合注释以用于扩充指定类或接口的方法。来自mixinSource 的所有注解都将覆盖target(或其超类型)具有的注解。

    【讨论】:

    • 效果很好,也很方便。谢谢!要添加的一件事是,如果杰克逊两次添加type 字段时遇到问题,只需在mixin 中添加它以忽略手动设置的类型:@JsonTypeId String getType();
    猜你喜欢
    • 2018-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-08
    • 2015-02-26
    • 1970-01-01
    • 1970-01-01
    • 2014-05-01
    相关资源
    最近更新 更多