Utilizzando Jackson ObjectMapper per serializzare il nome della classe in JSON, non la superclasse
-
19-09-2019 - |
Domanda
Nel seguente codice di Jackson / Java che serializza gli oggetti in JSON, sto ottenendo questo:
{"animal":{"x":"x"}}
Tuttavia, quello che in realtà voglio ottenere è questo:
{"dog":{"x":"x"}}
C'è qualcosa che posso fare per AnimalContainer in modo che ricevo il tipo di esecuzione ( "cane", "gatto") dell'oggetto, invece di "animale")? ( Modifica: Sono consapevole che il nome della mappa proviene dai nomi dei metodi getter- e setter-.) L'unico modo che posso pensare di fare è all'interno AnimalContainer a avere un attributo di ogni tipo di animale, hanno incastonatori e getter per tutti loro, e applicare che solo uno è valutato alla volta. Ma questo contrasta con l'obiettivo di avere la superclasse degli animali e sembra semplicemente sbagliato. E nel mio codice vero e proprio Io in realtà sono una dozzina di sottoclassi, non solo "cane" e "cat". C'è un modo migliore per fare questo (magari utilizzando le annotazioni in qualche modo)? Ho bisogno di una soluzione per la deserializzazione, pure.
public class Test
{
public static void main(String[] args) throws Exception
{
AnimalContainer animalContainer = new AnimalContainer();
animalContainer.setAnimal(new Dog());
StringWriter sw = new StringWriter(); // serialize
ObjectMapper mapper = new ObjectMapper();
MappingJsonFactory jsonFactory = new MappingJsonFactory();
JsonGenerator jsonGenerator = jsonFactory.createJsonGenerator(sw);
mapper.writeValue(jsonGenerator, animalContainer);
sw.close();
System.out.println(sw.getBuffer().toString());
}
public static class AnimalContainer
{
private Animal animal;
public Animal getAnimal() {return animal;}
public void setAnimal(Animal animal) {this.animal = animal;}
}
public abstract static class Animal
{
String x = "x";
public String getX() {return x;}
}
public static class Dog extends Animal {}
public static class Cat extends Animal {}
}
Soluzione
Come per questo announement , Jackson 1.5 implementa completa gestione di tipo polimorfo, e tronco ora è che il codice integrato.
Ci sono due modi semplici per fare questo lavoro:
- Aggiungi @JsonTypeInfo annotazione nei supertipo (Animale qui) o
- Configura oggetto mapper chiamando ObjectMapper.enableDefaultTyping () (ma se è così, animali deve essere di tipo astratto)
Altri suggerimenti
Questa non è probabilmente la risposta che state cercando, ma ci sono piani per implementare una corretta "deserializzazione polimorfa" (e il sostegno necessari sul serializzazione per esso), per Jackson versione 1.4 o giù di lì (cioè non il prossimo, 1.3, ma quello dopo).
Per la versione corrente, è necessario implementare serializzatori personalizzati / deserializzatore: Io probabilmente basta definire metodo factory per la deserializzazione, e digitare getter per serializzatore (definire 'getAnimalType' o qualsiasi altra cosa nella classe base astratta come astratto, override in sotto-classi -?. o anche solo attuare nella classe base, nome della classe di uscita di classe esempio)
In ogni caso, nel caso in cui è importante, qui ci sono problemi di fondo WRT attuazione movimentazione di sottoclassi con JSON, e senza linguaggio di schema (dato JSON in realtà non hanno ampiamente usato uno):
- come separare dati (valori di proprietà fagiolo) da metadati (informazioni di tipo necessarie solo per costruire sottoclassi propri) - devono essere tenuti separati, ma JSON come formato non ha modo di definire (potrebbe usare convenzione di denominazione)
- come aggiungere annotazioni adeguate per generare e utilizzare tali metadati; e senza dipendere da caratteristiche specifiche della lingua (non dovrebbe avere per legare a nomi di classi Java per esempio)
Si tratta di problemi risolvibili, ma non banalmente facile da risolvere. : -)
Questo è l'unico modo che posso pensare di farlo, ed è brutto. C'è un modo migliore?
@JsonWriteNullProperties(false)
public static class AnimalContainer
{
private Animal animal;
public Animal getCat()
{
return animal instanceof Cat ? animal : null;
}
public void setCat(Cat cat)
{
this.animal = cat;
}
public Animal getDog()
{
return animal instanceof Dog ? animal : null;
}
public void setDog(Dog dog)
{
this.animal = dog;
}
public Animal getFish()
{
return animal instanceof Fish ? animal : null;
}
public void setFish(Fish fish)
{
this.animal = fish;
}
}