web-dev-qa-db-fra.com

Désérialisation de Jackson sur plusieurs types

J'ai une classe abstraite appelée Instance puis deux implémentations de cela, UserInstance et HardwareInstance. Le problème que j'ai est que lorsque j'appelle le point de terminaison de repos pour un @POST Dans la base de données, je voulais idéalement que ce soit comme .../rest/soexample/instance/create Où l'instance est passée à REST endpoint. Si Instance n'était pas abstrait avec plus d'une implémentation, ce serait bien, mais comme j'en ai 2, j'obtiens une erreur Jackson.databind.

"problème: les types abstraits doivent être mappés à des types concrets, avoir un désérialiseur personnalisé ou être instanciés avec des informations de type supplémentaires"

Après avoir recherché une solution, j'ai trouvé une réponse SO qui disait que je pouvais utiliser quelque chose comme:

@JsonDeserialize(as=UserInstance.class)

Mais il semble que cela ne soit utile que s'il existe une implémentation de la classe abstraite. En supposant que je ne peux pas l'appeler deux fois car il n'y aurait aucun moyen pour lui de décider de quel type d'instance il s'agirait.

Je me demande donc quelle est la meilleure façon de gérer cette situation? Dois-je créer différents points de terminaison? Comme:

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

Je ne suis pas trop sûr car je suis un noobie @ REST choses liées, bien qu'en essayant activement d'apprendre. Merci!

16
erp

Voici ce que j'ai fait dans votre même cas:

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

Vous annotez Instance avec @JsonDeserialize(using = InstanceDeserializer.class) pour indiquer la classe à utiliser pour désérialiser la classe abstraite. Vous devez alors indiquer que chaque classe enfant sera désérialisée as eux-mêmes, sinon ils utiliseront le désérialiseur de classe parent et vous obtiendrez un StackOverflowError.

Enfin, à l'intérieur du InstanceDeserializer vous mettez la logique à désérialiser dans l'une ou l'autre classe enfant (checkConditionsForUserInstance() par exemple).

25
carcaret