Make @JsonTypeInfo property optional

匿名 (未验证) 提交于 2019-12-03 02:31:01

问题:

I'm using @JsonTypeInfo to instruct Jackson to look in the @class property for concrete type information. However, sometimes I don't want to have to specify @class, particularly when the subtype can be inferred given the context. What's the best way to do that?

Here's an example of the JSON:

{      "owner": {"name":"Dave"},     "residents":[         {"@class":"jacksonquestion.Dog","breed":"Greyhound"},         {"@class":"jacksonquestion.Human","name":"Cheryl"},         {"@class":"jacksonquestion.Human","name":"Timothy"}     ] } 

and I'm trying to deserialize them into these classes (all in 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; } } 

using this config:

@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); 

As you can see, the owner is declared as a Human, not an Animal, so I want to be able to omit @class and have Jackson infer the type as it normally would.

When I run this though, I get

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) 

Since "owner" doesn't specify @class.

Any ideas? One initial thought I had was to use @JsonTypeInfo on the property rather than the type. However, this cannot be leveraged to annotate the element type of a list. (Incorrect, see answer)

回答1:

You probably should not actually do this -- it seems like a micro-optimization for special cases, complicating life -- but if you really think you do want to, you can try adding @JsonTypeInfo override on Human. Annotations are inheritable in Jackson, and you can override definitions. In this case which one gets used then depends on declared type: so anything declared as Human would see annotation on Human; and anything declared as Animal only one in `Animal.

One tricky case is the root value (value you directly serialize): since there is no declared type, it will use the runtime type. And this will probably not work the way you want.

Another possibility is sub-classing AnnotationIntrospector: you can change the handling of @JsonTypeInfo there as well; just see what JacksonAnnotationIntrospector does, override behavior as applicable.



回答2:

So it turns out that I had misunderstood the Javadoc for @JsonTypeInfo. When I said in my question

One initial thought I had was to use @JsonTypeInfo on the property rather than the type. However, this cannot be leveraged to annotate the element type of a list.

I was basing that on this quote from the Javadoc:

When used for properties (fields, methods), this annotation applies to values: so when applied to structure types (like Collection, Map, arrays), will apply to contained values, not the container; for non-structured types there is no difference. (...) There is no per-property way to force type information to be included for type of container (structured type); for container types one has to use annotation for type declaration.

I somehow misread that to mean the opposite; that the type info would apply to the container and not the elements. Obviously that was wrong. So I was able to fix this by using the following:

public class Household {    //...     @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")    public void setResidents(List<Animal> residents) {        this.residents = residents;     } } 

Now @class is only required for Animals that are specified in the residents property.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!