Jackson多态反序列化避免了基类型的类型信息

时间:2018-06-07 11:56:21

标签: jackson polymorphism

过去几天我一直在研究这个问题,尽管我觉得我已接近实现,但我不能100%确定它是否可行。

简而言之,我想要实现的目标是高级使用"多态反序列化"满足以下要求:

  1. 使用注释不是一种选择
  2. 出于向后兼容的原因,必须省略基类的类型信息(在序列化和反序列化中)
  3. 我们说我有以下实体,例如:

    public class Request
    {
        public String currency;
    }
    
    public class RequestA extends Request
    {
        public String fieldA;
    }
    
    public class RequestB extends Request
    {
        public String fieldB;
    }
    

    我也有这个自定义类型ID解析器:

    public class CustomTypeIdResolver extends TypeIdResolverBase
    {
        private JavaType superType;
    
        @Override
        public void init(JavaType baseType)
        {
            superType = baseType;
        }
    
        @Override
        public String idFromValue(Object value)
        {
            return idFromValueAndType(value, value.getClass());
        }
    
        @Override
        public String idFromValueAndType(Object value, Class<?> suggestedType)
        {
            String typeId = null;
            switch (suggestedType.getSimpleName()) {
            case "RequestA":
                typeId = "A";
                break;
            case "RequestB":
                typeId = "B";
            }
            return typeId;
        }
    
        @Override
        public JavaType typeFromId(DatabindContext context, String id) {
            Class<?> subType = null;
            switch (id) {
            case "A":
                subType = RequestA.class;
                break;
            case "B":
                subType = RequestB.class;
            }
            return context.constructSpecializedType(superType, subType);
        }
    
        @Override
        public JsonTypeInfo.Id getMechanism()
        {
            return JsonTypeInfo.Id.CUSTOM;
        }
    }
    

    这个自定义注释introspector:

    public class CustomAnnotationIntrospector extends JacksonAnnotationIntrospector
    {
        @Override
        public TypeResolverBuilder<?> findTypeResolver(MapperConfig<?> config,
                AnnotatedClass ac, JavaType baseType)
        {
            // Preserve default behaviour
            TypeResolverBuilder<?> typeResolver = super.findTypeResolver(config, ac, baseType);
            if (typeResolver != null ) {
                return typeResolver;
            }
    
            if (ac.getAnnotated().equals(RequestA.class)) {
                typeResolver = new StdTypeResolverBuilder();
                typeResolver = typeResolver.init(JsonTypeInfo.Id.CUSTOM, new CustomTypeIdResolver());
                typeResolver = typeResolver.inclusion(JsonTypeInfo.As.PROPERTY);
                typeResolver.typeProperty("@type");
            } else if (ac.getAnnotated().equals(RequestB.class)) {
                typeResolver = new StdTypeResolverBuilder();
                typeResolver = typeResolver.init(JsonTypeInfo.Id.CUSTOM, new CustomTypeIdResolver());
                typeResolver = typeResolver.inclusion(JsonTypeInfo.As.PROPERTY);
                typeResolver.typeProperty("@type");
            }
    
            return typeResolver;
        }
    }
    

    当我像这样运行时:

    @Test
    public void test() throws Exception
    {
        Request r = new Request();
        r.currency = "R";
    
        RequestA a = new RequestA();
        a.currency = "A";
        a.fieldA = "field A";
    
        RequestB b = new RequestB();
        b.currency = "B";
        b.fieldB = "field B";
    
        ObjectMapper mapper = new ObjectMapper();
        mapper.setAnnotationIntrospector(new CustomAnnotationIntrospector());
    
        String contentR = write(mapper, r);
        String contentA = write(mapper, a);
        String contentB = write(mapper, b);
    
        Request readR = read(mapper, contentR);
        RequestA readA = (RequestA) read(mapper, contentA);
        RequestB readB = (RequestB) read(mapper, contentB);
    
        assertEquals(r, readR);
        assertEquals(a, readA);
        assertEquals(b, readB);
    }
    
    private static String write(ObjectMapper mapper, Request r) throws Exception
    {
        String content = mapper.writeValueAsString(r);
        System.out.println(content);
        return content;
    }
    
    private static Request read(ObjectMapper mapper, String value) throws Exception
    {
        return mapper.readValue(value, Request.class);
    }
    

    我得到了这个输出:

    {"currency":"R"}
    {"@type":"A","currency":"A","fieldA":"field A"}
    {"@type":"B","currency":"B","fieldB":"field B"}
    
    
    com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "@type" (class cvo.experiments.Request), not marked as ignorable (one known property: "currency"])
     at [Source: {"@type":"A","currency":"A","fieldA":"field A"}; line: 1, column: 11] (through reference chain: cvo.experiments.Request["@type"])
    
    
     at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:62)
     at com.fasterxml.jackson.databind.DeserializationContext.handleUnknownProperty(DeserializationContext.java:834)
     at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:1094)
     at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1470)
     at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1448)
     at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:282)
     at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:140)
     at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3798)
     at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2842)
     at cvo.experiments.MapperTest.read(MapperTest.java:200)
     at cvo.experiments.MapperTest.without_annotations_best(MapperTest.java:183)
     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
     at java.lang.reflect.Method.invoke(Method.java:498)
     at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
     at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
     at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
     at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
     at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
     at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
     at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
     at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
     at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
     at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
     at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
     at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
     at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
     at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
     at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
     at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
     at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
     at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
    

    这就是发生的事情:

    1. 所有实体都已按预期序列化 - 基本类型不包含类型信息,子类型也包含
    2. 基本类型已正确反序列化回对象
    3. 它无法反序列化子类型,即使(据我所知)自定义注释introspector提供类型字段和知道如何获得正确类型的自定义类型ID解析器
    4. 我想知道我试图实现的目标是否可行。如果是的话,我的做法有什么不对?

0 个答案:

没有答案