如何使用 Jackson 反序列化匿名摘要 class?
How to deserialize an anonymous abstract class with Jackson?
我最近开始使用 Jackson,因为朋友推荐它,所以我决定创建这个 Item 对象,这样我就可以尝试序列化和反序列化,尽管我在抽象中出现异常 class反序列化时。此处列出的 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;
}
}
可保存界面由
组成
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 寻找答案时,我尝试启用 return 相同异常的默认类型,并尝试实现您自己的反序列化器。我真的找不到很好的资源来展示如何创建自己的反序列化器。这让我相信它可能与通过扩展 StdDeserializer 来实现你自己的反序列化器有关,但我真的不知道怎么做。我希望在执行 Saveable.load(Paths.get("./data", "other", "shop"), "Testing", Prefix.SHOP, GiftShop.Item.class)
时 return 一个项目,但此刻我遇到异常。我在这个 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 中编写了这一切。感谢您的宝贵时间,希望我能提供所有需要的信息。
问题是反序列化器不知道它应该从 JSON 字符串创建什么样的 class。您传递给它的 class 参数是一个抽象 class 并且您不能在 java.
中实例化一个抽象 class
您必须告诉对象映射器所需的类型 class。在 Item.class 上使用 @JsonTypeInfo
注释,这将在 JSON 字符串中存储额外的类型信息。
但不幸的是它仍然不够,因为匿名内部class只有一个临时对象类型,在方法之外是看不到的,所以即使它的类型信息存储在JSON 字符串,以后无法恢复。
因此您必须至少将实际 class 定义为内部 class。而且您仍然必须定义如何存储类型信息。
这里有一个很好的指南:
我最近开始使用 Jackson,因为朋友推荐它,所以我决定创建这个 Item 对象,这样我就可以尝试序列化和反序列化,尽管我在抽象中出现异常 class反序列化时。此处列出的 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;
}
}
可保存界面由
组成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 寻找答案时,我尝试启用 return 相同异常的默认类型,并尝试实现您自己的反序列化器。我真的找不到很好的资源来展示如何创建自己的反序列化器。这让我相信它可能与通过扩展 StdDeserializer 来实现你自己的反序列化器有关,但我真的不知道怎么做。我希望在执行 Saveable.load(Paths.get("./data", "other", "shop"), "Testing", Prefix.SHOP, GiftShop.Item.class)
时 return 一个项目,但此刻我遇到异常。我在这个 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 中编写了这一切。感谢您的宝贵时间,希望我能提供所有需要的信息。
问题是反序列化器不知道它应该从 JSON 字符串创建什么样的 class。您传递给它的 class 参数是一个抽象 class 并且您不能在 java.
中实例化一个抽象 class您必须告诉对象映射器所需的类型 class。在 Item.class 上使用 @JsonTypeInfo
注释,这将在 JSON 字符串中存储额外的类型信息。
但不幸的是它仍然不够,因为匿名内部class只有一个临时对象类型,在方法之外是看不到的,所以即使它的类型信息存储在JSON 字符串,以后无法恢复。
因此您必须至少将实际 class 定义为内部 class。而且您仍然必须定义如何存储类型信息。
这里有一个很好的指南: