Как десериализовать анонимный абстрактный класс с Джексоном? - PullRequest
0 голосов
/ 14 октября 2018

Я недавно начал использовать Джексона в качестве друга, порекомендовал его, поэтому я решил создать этот объект 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)

Когда я пытался найти ответ на этот вопрос в Google, я попытался включить тип по умолчанию, который возвращает то же исключение, и попытаться реализовать свой собственный десериализатор.Я не мог найти отличный источник, чтобы показать, как создать свой собственный десериализатор.Это заставляет меня верить, что, возможно, это что-то вроде реализации собственного десериализатора путем расширения StdDeserializer, но я не могу понять, как это сделать.Я ожидаю вернуть предмет при выполнении Saveable.load(Paths.get("./data", "other", "shop"), "Testing", Prefix.SHOP, GiftShop.Item.class), но в данный момент получаю исключение.Я проверяю все это в этом тесте Спока

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.Спасибо за ваше время и надеюсь, что смогу предоставить всю необходимую информацию.

1 Ответ

0 голосов
/ 14 октября 2018

Проблема в том, что десериализатор не знает, какой класс он должен создать из строки JSON.Аргумент класса, который вы передаете, является абстрактным классом, и вы не можете создать экземпляр абстрактного класса в Java.

Вы должны сообщить объекту отображения тип желаемого класса.Используйте @JsonTypeInfo аннотацию для Item.class, которая будет хранить дополнительную информацию о типе в строке JSON.

Но, к сожалению, этого все еще недостаточно, поскольку у анонимного внутреннего класса есть только временный объектный тип, онне может быть виден вне метода, поэтому даже информация о его типе хранится в строке JSON, его нельзя восстановить позже.

Таким образом, вы должны определить реальный класс как минимум как внутренний класс.И вам все еще нужно определить, как хранить информацию о типах.

Здесь есть очень хорошее руководство:

Наследование с Джексоном

...