I am glued with some Jackson polymorphic problem.
I work on a web JDR Character Editor personnal project. I use Springboot and try to stuck with the phylosophy. Moreover, I try to make some independent packages, because of study-case for my real work (another springboot project).
With no Jackson configuration, I have no problem for serialization of a Competence. But when I try to get back any modification on the web editor, so when Jackson make a deserialization of a Competence, problems occur with "dependance" property.
Here are my classes:
The one I try to serialize/deserialize:
public class Competence implements Composante, ComposanteTemplate { public enum Categorie { APPRENTI, COMPAGNON } private String nom; private String description; private Categorie categorie; private Chapitre chapitre; private AttributTemplate dependance; private List sousCompetences = new ArrayList(); public String getNom() { return nom; } public void setNom(String nom) { this.nom = nom; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public Competence getTemplate() { return this; } public Categorie getCategorie() { return categorie; } public void setCategorie(Categorie categorie) { this.categorie = categorie; } public Chapitre getChapitre() { return chapitre; } public void setChapitre(Chapitre chapitre) { this.chapitre = chapitre; } public AttributTemplate getDependance() { return dependance; } public void setDependance(AttributTemplate dependance) { this.dependance = dependance; } public List getSousCompetences() { return sousCompetences; } public void setSousCompetences(List sousCompetences) { this.sousCompetences = sousCompetences; } public boolean isOuverte() { return !sousCompetences.isEmpty(); } }
The superclass of the property I have a problem with:
public interface AttributTemplate extends ComposanteTemplate {}
The two subclasses which could be use for Competence#dependance property:
public enum Carac implements AttributTemplate, Attribut { FORT(Type.PHYSIQUE), AGILE(Type.PHYSIQUE), RESISTANT(Type.PHYSIQUE), OBSERVATEUR(Type.PHYSIQUE), SAVANT(Type.MENTALE), RUSE(Type.MENTALE), TALENTUEUX(Type.MENTALE), CHARMEUR(Type.MENTALE); public enum Type { PHYSIQUE, MENTALE } public final Type type; public final String nom = name().toLowerCase(); private String description; Carac(Type type) { this.type = type; } @Override public String getNom() { return nom; } @Override public String getDescription() { return description; } @Override public Carac getTemplate() { return this; } public void setDescription(String description) { this.description = description; } }
public enum ArtTemplate implements AttributTemplate { ART_GUERRIER(2, 1), ART_ETRANGE(1, 2), ART_GUILDIEN(1, 1); public static final String ART_PREFIX = "ART"; public final String nom = name().toLowerCase().replace("_", " "); public final int nbCaracsPhysiques; public final int nbCaracsMentales; private String description; ArtTemplate(int nbCaracsPhysiques, int nbCaracsMentales) { this.nbCaracsMentales = nbCaracsMentales; this.nbCaracsPhysiques = nbCaracsPhysiques; } @Override public String getNom() { return nom; } @Override public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public int getNbCaracs() { return nbCaracsPhysiques + nbCaracsMentales; } }
The result json (and then the json I send) is:
{"nom":"Comp_1489746646510","description":"ezbuixnwrclfvmgwdviubcauenzytpzzvumnohwyhpuynxaqhkjdbqygtrmbtlschthovuyoiolkauucwokkfjnaujnufshrjboykuqce","categorie":"APPRENTI","chapitre":"GUERRE","dependance":"ART_ETRANGE","ouverte":false,"sousCompetences":[]}
QUESTION: I understand that my problem is caused by the abstract relation AttributTemplate, and then when Jackson try to deserialize, he does not know which of Carac or ArtTemplate class to use. I try to keep unchanged Competence (Competence come from an external jar), so no annotation on this class is possible.
I've tried many of the solutions I found (Jackson 1.5: Polymorphic Type Handling, first steps ) and the only one which has worked was to define a DeserializationProblemHandler
mapper.addHandler(new DeserializationProblemHandler() {
@Override
public Object handleMissingInstantiator(DeserializationContext ctxt, Class<?> instClass, JsonParser p, String msg) throws IOException {
if (instClass == AttributTemplate.class) {
String name = p.getText();
return !name.startsWith(ArtTemplate.ART_PREFIX) ? Carac.valueOf(name) : ArtTemplate.valueOf(name);
}
return super.handleMissingInstantiator(ctxt, instClass, p, msg);
}
});
But I feel bad with this solution, because I am sure there is an other beautiful one.
So is it possible to configure the mapper in order that he is able to determine which of Carac or ArtTemplate he must use to get AttributTemplate?
EDIT: I managed to have this:
{"nom":"Comp_1489756873433","description":"kruzueemlwisibshlkotasayfkhdqkqolvhlqgsnntndkpvbmmgklqysabiakaolempmupeyiqaztdcrhwimdksgzybbdzttwnwqjxhfo","categorie":"COMPAGNON","chapitre":"GUERRE","dependance":["mova.ged.perso.inne.Carac","AGILE"],"ouverte":true,"sousCompetences":[...]}
by configuring like this the mapper
abstract class CompetenceMixIn { private AttributTemplate dependance; @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.EXISTING_PROPERTY, property="dependance") @JsonSubTypes({ @JsonSubTypes.Type(value = Carac.class, name = "carac"), @JsonSubTypes.Type(value = ArtTemplate.class, name = "artTemplate") }) public void setDependance(AttributTemplate dependance) { this.dependance = dependance; } }
ObjectMapper mapper = jsonConverter.getObjectMapper(); mapper.addMixIn(Competence.class, CompetenceMixIn.class);
As you could see, I'm still parasited with the array that wrapped dependance
value. I would (...)"dependance": "AGILE", (...)
not (...)"dependance":["mova.ged.perso.inne.Carac", "AGILE"], (...)
And I don't know what to change in order to have this.