Vous avez deux problèmes différents ici : créer un objet de type inconnu et faire quelque chose de significatif avec lui par la suite. Si le type créé dépend vraiment de la valeur d'un noeud dans le json, vous n'aurez pas le temps de créer une correspondance entre ce String et la classe qu'il créera. Un énorme bloc if-then-else ne sera pas utile au-delà de deux ou trois classes.
Création
Vous pouvez créer un singleton de registre que vous utiliserez pour maintenir cette correspondance.
public enum ClassByNodeMapping {
INSTANCE;
final Map<String, Class<?>> mapping = new HashMap<>();
public void addMapping(String nodeValue, Class<?> clazz) {
mapping.put(nodeValue, clazz);
}
public Class<?> getMapping(String nodeValue) {
return mapping.get(nodeValue);
}
}
Vous pouvez remplir ce formulaire par classe :
@Data
class Model1 {
static {
ClassByNodeMapping.INSTANCE.addMapping("model1", Model1.class);
}
private String model1Value;
}
Mais même dans ce cas, vous devrez instancier Model1
une fois avant d'être enregistré (pour garantir que la classe a été chargée). Votre code client ressemblerait donc à ceci :
class Scratch {
static {
// need to instantiate the models once to trigger registration
Model1 model = new Model1();
Model2 model2 = new Model2();
}
private static final ObjectMapper mapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
public static void main(String[] args) throws IOException {
String json1 = "{\"type\": \"model1\", \"model1Value\": \"m1\"}";
String json2 = "{\"type\": \"model2\", \"model2Value\": \"m2\"}";
System.out.println(deserialize(json1));
System.out.println(deserialize(json2));
}
private static Object deserialize(String json) throws IOException {
JsonNode jsonNode = mapper.readTree(json);
Class<?> type = ClassByNodeMapping.INSTANCE.getMapping(jsonNode.get("type").textValue());
return mapper.readValue(json, type);
}
}
Traitement
Vous avez maintenant créé des objets, mais comme ils sont de type Object
Il n'y a pas grand-chose à en faire. Dans les commentaires à votre question, vous avez dit quelque chose comme "reserialize", je ne suis pas sûr de ce que cela signifie ; le concept général est la consommation ici - l'objet est créé et ensuite quelque chose est fait avec lui. Vous pouvez utiliser le concept de visiteur de l'autre réponse ici, mais je le trouve quelque peu confus.
Vous pouvez étendre le mappage en incluant également un gestionnaire. En général, il est préférable de séparer les deux, mais dans ce cas, cela peut s'avérer nécessaire pour préserver la sécurité des types.
enum ClassByNodeMapping {
INSTANCE;
final Map<String, Class<?>> mapping = new HashMap<>();
final Map<Class<?>, Consumer<?>> handlerMapping = new HashMap<>();
public <T>void addMapping(String nodeValue, Class<T> clazz, Consumer<T> handler) {
mapping.put(nodeValue, clazz);
handlerMapping.put(clazz, handler);
}
public Class<?> getMapping(String nodeValue) {
return mapping.get(nodeValue);
}
public Consumer<Object> getHandler(Class<?> clazz) {
return (Consumer<Object>) handlerMapping.get(clazz);
}
}
Votre code d'enregistrement ressemble donc à ceci
@Data
class Model1 {
static {
// you'd probably not want to do the registration here,
// assuming your handler code is outside
ClassByNodeMapping.INSTANCE.addMapping("model1", Model1.class, Model1::handle);
}
private String model1Value;
private static void handle(Model1 m1) {
System.out.printf("handling as model1: %s%n", m1);
}
}
Et votre code client (test)
public static void main(String[] args) throws IOException {
String json1 = "{\"type\": \"model1\", \"model1Value\": \"m1\"}";
String json2 = "{\"type\": \"model2\", \"model2Value\": \"m2\"}";
handle(json1);
handle(json2);
}
private static void handle(String json) throws IOException {
JsonNode jsonNode = mapper.readTree(json);
Class<?> type = ClassByNodeMapping.INSTANCE.getMapping(jsonNode.get("type").textValue());
Consumer<Object> handler = ClassByNodeMapping.INSTANCE.getHandler(type);
Object o = mapper.readValue(json, type);
handler.accept(o);
}
Mise en garde
Je considérerais le concept dans son ensemble comme une odeur de code. Je m'efforcerais certainement de faire en sorte que le canal entrant ne contienne que des données d'une valeur de 1,5 million d'euros.