【问题标题】:Deserialization of subclasses without type information没有类型信息的子类的反序列化
【发布时间】:2018-02-03 15:07:32
【问题描述】:

我有一个表示各种有限域值的类(类似于枚举):

public abstract class AbstractEnum {
    private String id;
    // ...
}

具体类 EnumA, EnumB 扩展 AbstractEnum。 业务对象可能看起来像

public class Obj {
    private EnumA a;
    private EnumB b;
    // ...
}

EnumA, EnumB 的实例由工厂管理。在反序列化Obj 时,我想调用工厂而不是构造函数。具体类型(EnumAEnumB)无法从枚举值的 JSON 表示中推断出来,因为它们只包含它们的 ID,而是从 Obj 中的相应属性类型推断出来的。

我尝试在AbstractEnum 上使用@JsonCreator@JsonDeserialize(带转换器),但创建者/转换器似乎无法使用该属性类型。似乎我必须使用@JsonTypeInfo 来序列化额外的类型信息(这似乎是多余的),或者为每个具体的AbstractEnum 子类定义一个创建者/转换器,其中不止两个。 还有其他方法吗?

编辑EnumA 的实例可以与EnumB 的实例具有相同的 ID,因此该 ID 不能用于推断类型。

【问题讨论】:

  • 您应该为AbstractEnum 实现自定义JsonDeserializer。在里面你可以使用你的工厂。
  • 因为EnumAEnumBObj中有自己的变量名,那么JSON序列化应该能够判断字段a和字段b是否存在.基本上字段名(也就是变量名)会区分它们。

标签: java jackson


【解决方案1】:

您可以使用字段名称来区分类型,而不必依赖工厂或其他任何东西。我在这里假设名为a 的字段将始终引用EnumA 类型,依此类推。下面是一些使用您的代码并尝试显示单独的字段名称可能会起作用的测试代码。

public class SpikeJson {

    public static abstract class AbstractEnum {

        private final String id;

        protected AbstractEnum(String id) {
            this.id = id;
        }

        @Override
        public String toString() {
            return "AbstractEnum{" + "id=" + id + '}';
        }

    }

    public static class EnumA extends AbstractEnum {

        public EnumA() {
            super("enuma");
        }

    }

    public static class EnumB extends AbstractEnum {

        public EnumB() {
            super("enumb");
        }

    }

    public static class EnumC extends AbstractEnum {

        public EnumC() {
            super("enumc");
        }

    }

    public static class Obj {

        @JsonProperty
        private EnumA a;
        @JsonProperty
        private EnumB b;
        @JsonProperty
        private EnumC c;

        @Override
        public String toString() {
            return "Obj{" + "a=" + a + ", b=" + b + ", c=" + c + '}';
        }

    }

    public static void main(String[] args) throws IOException {

        //{
        //  "a": {},
        //  "b": null
        //}
        String json = "{\n"
                + " \"a\": {},\n"
                + " \"b\": null\n"
                + "}";

        ObjectMapper om = new ObjectMapper();
        Obj obj = om.readValue(json, Obj.class);
        System.out.println("obj = " + obj);

    }

}

这段代码的输出是

obj = Obj{a=AbstractEnum{id=enuma}, b=null, c=null}

这表明 JSON 已正确反序列化。

【讨论】:

  • 是的,反序列化Obj 有效,但是标准反序列化会创建枚举类的新实例,而我需要确保每个枚举类和 ID 组合最多有一个实例。我使用ContextualDeserializer 解决了它,请参阅我的答案。
【解决方案2】:

我按照here 的建议使用ContextualDeserializer 解决了它:在createContextual() 中我获取具体类,在deserialize() 中我调用我的工厂以获取基于targetClass 和ID 的实例。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-12-01
    • 2013-10-18
    • 2016-07-16
    • 2021-06-25
    相关资源
    最近更新 更多