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