【问题标题】:How to deserialize an anonymous abstract class with Jackson?如何用杰克逊反序列化匿名抽象类?
【发布时间】:2018-10-14 05:40:41
【问题描述】:

我最近开始使用 Jackson,因为朋友推荐它,所以我决定创建这个 Item 对象,以便我可以玩转序列化和反序列化,尽管在反序列化时出现了抽象类的异常。此处列出的 Item 对象

public static abstract class Item implements Saveable, Serializable, Useable {
    private static final long serialVersionUID = 45612874562156L;
    private final String nameId;
    String name;
    String description;
    int value;
    public Item(String name, String description, int value) {
        this.name = name;
        this.nameId = name;
        this.description = description;
        this.value = value;
    }
    @Override
    public void use() {}
    @Override
    public void save(Path directory) {
        save(directory, nameId, Prefix.SHOP, this);
    }
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() { return description; }

    public void setDescription(String description) {
        this.description = description;
    }

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }
}

Saveable 接口由

public interface Saveable {

    ObjectMapper SAVEMAPPER = new ObjectMapper();

    void save(Path outputDirectory);

    default <T extends Serializable, E extends Enum<E>> void save(Path outputDirectory, long id, E prefixType, T object) {
        save(outputDirectory, String.valueOf(id), prefixType, object);
    }
    default <T extends Serializable, E extends Enum<E>> void save(Path outputDirectory, String id, E prefixType, T object) {
        outputDirectory.toFile().mkdir();
        try {
            SAVEMAPPER.writeValue(Paths.get(outputDirectory.toString(), id+prefixType.toString()+".json").toFile(), object);
        } catch (IOException e) {
            throw new CouldNotSaveFileException(e.getMessage());
        }
    }
    static <T, E extends Enum<E>> T load(Path outputDirectory, String id, E prefixType, Class<? extends T> clazz) throws CouldNotLoadFileException {
        try {
            SAVEMAPPER.enableDefaultTyping();
            return SAVEMAPPER.readValue(Paths.get(outputDirectory.toString(), id+prefixType.toString()+".json").toFile(), clazz);
        } catch (IOException e) {
            throw new CouldNotLoadFileException(e.getMessage());
        }
    }
    static <T, E extends Enum<E>> T load(Path outputDirectory, long id, E prefix, Class<? extends T> clazz) throws CouldNotLoadFileException {
        return load(outputDirectory, String.valueOf(id), prefix, clazz);
    }
    class CouldNotLoadFileException extends RuntimeException {
            CouldNotLoadFileException(String desciption) {
                super("Could not load file\n" + desciption);
            }
    }
    class CouldNotSaveFileException extends RuntimeException {
        CouldNotSaveFileException(String desciption) {
            super("Could not save file\n" + desciption);
        }
    }
}

Useable 接口只是一个名为 use() 的抽象方法。 我对它进行序列化没有问题,所以当我执行new Item("Foo", "Bar", 10){}.save() 时,它会正确序列化(我假设)到{"name":"Foo","description":"Bar","value":10} 这很好,但是在反序列化过程中我遇到了这个异常

me.James.misc.Saveable$CouldNotLoadFileException: Could not load file
Unexpected token (START_OBJECT), expected START_ARRAY: need JSON Array to contain As.WRAPPER_ARRAY type information for class me.James.commands.GiftShop$Item
 at [Source: (File); line: 1, column: 1]

    at me.James.misc.Saveable.load(Saveable.java:31)
    at me.James.commands.GiftShopSpec.creating, saving, and loading an item(GiftShopSpec.groovy:16)

当试图用谷歌搜索这个问题的答案时,我尝试启用返回相同异常的默认类型,并尝试实现您自己的反序列化器。我真的找不到很好的资源来展示如何创建自己的反序列化器。这相信我相信它可能通过扩展 StdDeserializer 来实现你自己的反序列化器,但我真的不知道如何。我希望在执行Saveable.load(Paths.get("./data", "other", "shop"), "Testing", Prefix.SHOP, GiftShop.Item.class) 时返回一个项目,但目前我遇到了一个异常。我在这个 Spock 测试中测试了所有这些

class GiftShopSpec extends Specification {
    void "creating, saving, and loading an item"() {
        given: "an item with example values"
        GiftShop.Item sampleItem = new GiftShop.Item("Testing", "Test", 5) {}
        and: "saving the item"
        sampleItem.save(Paths.get("./data", "other", "shop"))
        when: "we load the item from disk"
        GiftShop.Item loadedItem = Saveable.load(Paths.get("./data", "other", "shop"), "Testing", Prefix.SHOP, GiftShop.Item.class);
        then: "we get an item from disk"
        sampleItem == loadedItem
    }
}

如果问题在于我没有创建自己的自定义反序列化器,而不是关于如何实现的资源会很棒。我正在用 Java 10 和 Jackson 2.9.7 版编写这一切。感谢您抽出宝贵时间,希望我能提供所需的所有信息。

【问题讨论】:

  • 欢迎来到 SO,请将异常/错误添加为问题的一部分,而不是作为图像或指向其他网站的链接。
  • 对不起,我没有看到图片?不过,我确实使用了指向 hastebin 的超链接。
  • 看看这个:codeblog.jonskeet.uk/2010/08/29/writing-the-perfect-question 问一个有效的问题
  • 哦,谢谢。我试试看。

标签: java jackson deserialization anonymous-class


【解决方案1】:

问题是反序列化器不知道它应该从 JSON 字符串创建什么样的类。你传递给它的类参数是一个抽象类,你不能在java中实例化一个抽象类。

您必须告诉 objectmapper 所需类的类型。在 Item.class 上使用 @JsonTypeInfo 注释,这会将附加的类型信息存储在 JSON 字符串中。

但遗憾的是还不够,因为匿名内部类只有一个临时的object-type,在方法外是看不到的,所以即使它的类型信息存储在JSON字符串中,以后也无法恢复。

因此,您必须至少将实际类定义为内部类。而且您仍然必须定义如何存储类型信息。

这里有一个很好的指南:

Inheritance with Jackson

【讨论】:

  • 非常感谢,Selindek 我会看看你提到的帖子。干杯!
猜你喜欢
  • 2015-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-12
  • 1970-01-01
  • 2015-04-07
  • 1970-01-01
  • 2019-11-15
相关资源
最近更新 更多