Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jackson deserialization on multiple types

Tags:

I have an abstract class called Instance and then two implementations of that, UserInstance and HardwareInstance. The issue I am having is that when I call the rest endpoint for a @POST into the database, I ideally wanted it to be like .../rest/soexample/instance/create where the instance is passed to the REST endpoint. If Instance wasn't abstract with more than one implementation it would be fine, but since I have 2 I am getting a Jackson.databind error.

" problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information"

After looking up a solution to this I found a SO answer that said I could use something like:

@JsonDeserialize(as=UserInstance.class)

But it seem's like that isonly useful if there is one implementation of the abstract class. Assuming I can't call it twice since there would be no way for it to decide which type of instance it would be.

So I am wondering what is the best way to handle this situation? Should I create different endpoints? Like:

.../rest/soexample/userinstance/create & .../rest/soexample/hardwareinstance/create

I am not too sure as I am a noobie @ REST related things, though actively trying to learn. Thanks!

like image 532
erp Avatar asked Sep 24 '15 16:09

erp


People also ask

Can Jackson ObjectMapper be reused?

This is safe as ObjectMapper is thread-safe after configuration. Caveat: As @StaxMan points out in the answer comments, this does NOT guarantee against further (re)configuration on the returned singleton.

What is Jackson deserialization?

Jackson is a powerful and efficient Java library that handles the serialization and deserialization of Java objects and their JSON representations. It's one of the most widely used libraries for this task, and runs under the hood of many other frameworks.

What is polymorphic deserialization?

A polymorphic deserialization allows a JSON payload to be deserialized into one of the known gadget classes that are documented in SubTypeValidator. java in jackson-databind in GitHub. The deserialized object is assigned to a generic base class in your object model, such as java. lang.

Does Jackson require default constructor?

Jackson uses default (no argument) constructor to create object and then sets value using setters. so you only need @NoArgsConstructor and @Setter.


1 Answers

Here is what I did in your same case:

@JsonDeserialize(using = InstanceDeserializer.class) public abstract class Instance {     //.. methods }  @JsonDeserialize(as = UserInstance.class) public class UserInstance extends Instance {     //.. methods }  @JsonDeserialize(as = HardwareInstance.class) public class HardwareInstance extends Instance {     //.. methods }  public class InstanceDeserializer extends JsonDeserializer<Instance> {     @Override     public Instance deserialize(JsonParser jp,  DeserializationContext ctxt) throws IOException, JsonProcessingException {         ObjectMapper mapper = (ObjectMapper) jp.getCodec();         ObjectNode root = (ObjectNode) mapper.readTree(jp);         Class<? extends Instance> instanceClass = null;         if(checkConditionsForUserInstance()) {             instanceClass = UserInstance.class;         } else {              instanceClass = HardwareInstance.class;         }            if (instanceClass == null){             return null;         }         return mapper.readValue(root, instanceClass );     } } 

You annotate Instance with @JsonDeserialize(using = InstanceDeserializer.class) to indicate the class to be used to deserialize the abstract class. You need then to indicate that each child class will be deserialized as themselves, otherwise they will use the parent class deserializer and you will get a StackOverflowError.

Finally, inside the InstanceDeserializer you put the logic to deserialize into one or another child class (checkConditionsForUserInstance() for example).

like image 148
carcaret Avatar answered Sep 18 '22 10:09

carcaret