3

@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属性而不是类型。 但是,这不能用于注释列表的元素类型。 (不正确,见答案)

4

2 回答 2

3

所以事实证明我误解了Javadoc 为@JsonTypeInfo. 当我在我的问题中说

我最初的想法是在属性上使用@JsonTypeInfo 而不是类型。但是,这不能用于注释列表的元素类型。

我是基于 Javadoc 中的这句话:

当用于属性(字段、方法)时,此注释适用于值:因此,当应用于结构类型(如 Collection、Map、数组)时,将适用于包含的值,而不是容器;对于非结构化类型没有区别。(...) 没有针对容器类型(结构化类型)强制包含类型信息的每个属性的方法;对于容器类型,必须使用注释进行类型声明。

我不知何故误读了相反的意思;类型信息将应用于容器而不是元素。显然那是错误的。所以我能够通过使用以下方法来解决这个问题:

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属性中指定的动物。

于 2012-06-06T17:36:57.393 回答
1

您可能实际上不应该这样做——这似乎是针对特殊情况的微优化,使生活复杂化——但如果你真的认为你确实想要这样做,你可以尝试@JsonTypeInfo在 Human 上添加覆盖。注释在 Jackson 中是可继承的,您可以覆盖定义。在这种情况下,使用哪一个取决于声明的类型:所以任何声明为的内容都会在;Human上看到注释。Human以及任何在“动物”中声明为Animal唯一的东西。

一个棘手的情况是根值(您直接序列化的值):由于没有声明类型,它将使用运行时类型。这可能不会按照您想要的方式工作。

另一种可能性是子类化AnnotationIntrospector:您也可以更改@JsonTypeInfo那里的处理;看看有什么JacksonAnnotationIntrospector,覆盖适用的行为。

于 2012-06-06T17:10:32.887 回答