我正在使用@JsonTypeInfo
指示杰克逊查看@class
属性中的具体类型信息。但是,有时我不想指定@class
,特别是在给定上下文的情况下可以推断出子类型时。 最好的方法是什么?
以下是JSON的一个示例:
{
"owner": {"name":"Dave"},
"residents":[
{"@class":"jacksonquestion.Dog","breed":"Greyhound"},
{"@class":"jacksonquestion.Human","name":"Cheryl"},
{"@class":"jacksonquestion.Human","name":"Timothy"}
]
}
我试图将它们反序列化为这些类(全部在jacksonquestion.*
中):
public class Household {
private Human owner;
private List<Animal> residents;
public Human getOwner() { return owner; }
public void setOwner(Human owner) { this.owner = owner; }
public List<Animal> getResidents() { return residents; }
public void setResidents(List<Animal> residents) { this.residents = residents; }
}
public class Animal {}
public class Dog extends Animal {
private String breed;
public String getBreed() { return breed; }
public void setBreed(String breed) { this.breed = breed; }
}
public class Human extends Animal {
private String name;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
使用此配置:
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
private static class AnimalMixin {
}
//...
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.getDeserializationConfig().addMixInAnnotations(Animal.class, AnimalMixin.class);
Household household = objectMapper.readValue(json, Household.class);
System.out.println(household);
正如您所看到的,所有者被声明为人类,而不是动物,因此我希望能够省略@class
并让杰克逊像往常一样推断出类型。
当我跑这个时,我得到了
org.codehaus.jackson.map.JsonMappingException: Unexpected token (END_OBJECT),
expected FIELD_NAME: missing property '@class' that is to contain type id (for class jacksonquestion.Human)
由于“所有者”未指定@class
。
有什么想法吗?我最初的想法是在属性上使用@JsonTypeInfo
而不是类型。 但是,这不能用于注释列表的元素类型。(不正确,请参阅答案)
答案 0 :(得分:3)
事实证明我误解了Javadoc for @JsonTypeInfo
。当我在我的问题中说时
我最初的想法是在属性上使用@JsonTypeInfo而不是类型。但是,这不能用于注释列表的元素类型。
我的观点基于Javadoc的这句话:
我在某种程度上误读了这意味着相反;类型信息将应用于容器而不是元素。显然那是错的。所以我能够通过使用以下内容来解决这个问题:当用于属性(字段,方法)时,此批注适用于值:因此,当应用于结构类型(如Collection,Map,arrays)时,将应用于包含的值,而不是容器;对于非结构化类型,没有区别。 (...)对于容器类型(结构化类型),没有强制类型信息的按属性方式;对于容器类型,必须使用注释进行类型声明。
public class Household {
//...
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
public void setResidents(List<Animal> residents) {
this.residents = residents;
}
}
现在只有在@class
属性中指定的动物才需要residents
。
答案 1 :(得分:1)
你可能不应该这样做 - 它似乎是针对特殊情况的微优化,使生活变得复杂 - 但如果你真的认为你确实想要,你可以尝试在Human上添加@JsonTypeInfo
覆盖。注释在Jackson中是可继承的,您可以覆盖定义。在这种情况下,哪一个被使用然后取决于声明的类型:所以声明为Human
的任何东西都会在Human
上看到注释;以及在{Animal。
Animal
的任何内容
一个棘手的情况是根值(直接序列化的值):由于没有声明的类型,它将使用运行时类型。这可能不会按你想要的方式工作。
另一种可能性是对AnnotationIntrospector
进行细分:您也可以在那里更改@JsonTypeInfo
的处理;只需查看JacksonAnnotationIntrospector
的作用,覆盖适用的行为。