Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

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!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
622 views
Welcome To Ask or Share your Answers For Others

1 Answer

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).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...