2009-08-24 8 views
10

Dans le code suivant Jackson/Java sérialise objets en JSON, je reçois ceci:utilisant Jackson ObjectMapper sérialiser le nom de sous-classe en JSON, et non la superclasse

{"animal":{"x":"x"}} 

Cependant, ce que je veux vraiment obtenir est la suivante:

{"dog":{"x":"x"}} 

y at-il quelque chose que je peux faire pour AnimalContainer pour que je sois le type d'exécution (« chien », « chat ») de l'objet, au lieu de « animal »)? (Modifier: Je suis conscient que le nom de la carte provient des noms getter- et setter-method.) La seule façon dont je peux penser à le faire est dans AnimalContainer d'avoir un attribut de chaque type d'animal, poseurs et acquéreurs pour chacun d'eux, et de faire en sorte qu'un seul est évalué à la fois. Mais cela va à l'encontre du but d'avoir la superclasse animale et semble juste faux. Et dans mon vrai code, j'ai en fait une douzaine de sous-classes, pas seulement "chien" et "chat". Y a-t-il une meilleure façon de faire cela (peut-être en utilisant des annotations en quelque sorte)? J'ai aussi besoin d'une solution pour la désérialisation.

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 {} 
} 

Répondre

9

Selon this announement, Jackson 1.5 implémente la gestion complète du type polymorphique, et le tronc intègre maintenant ce code.

Il y a deux façons simples de faire ce travail:

  • Ajouter une annotation @JsonTypeInfo dans supertype (Animal ici), OU
  • Configurer objet mappeur en appelant ObjectMapper.enableDefaultTyping() (mais si oui, Animal doit être de type abstrait)
+1

est-il un moyen de résoudre avec 'ObjectMapper.enableDefaultTyping()' sans avoir besoin d'Animal étant abstrait? J'utilise Jackson 1.9 et j'ai essayé cela mais ne fonctionne toujours pas ..... –

+0

La saisie par défaut peut utiliser différents types de critères, voir javadocs, y compris la définition personnalisée de ce à quoi il devrait s'appliquer. – StaxMan

0

C'est la seule façon que je peux penser pour le faire, et c'est moche. Y a-t-il un meilleur moyen?

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

Ceci est probablement pas la réponse que vous cherchez, mais il y a des plans pour mettre en œuvre une bonne « désérialisation polymorphes » (et le soutien nécessaires à la sérialisation pour lui), pour la version Jackson 1.4 ou si (et non la prochaine un, 1,3, mais un après). Pour la version actuelle, vous devez implémenter des sérialiseurs/désérialiseurs personnalisés: je définirais probablement la méthode d'usine pour la désérialisation, et tapez getter pour serializer (définissez 'getAnimalType' ou quoi que ce soit dans la classe de base abstraite comme abstrait, redéfinissez dans sub- classes - ou même simplement implémenter dans la classe de base, le nom de la classe de sortie de la classe d'instance?).

Quoi qu'il en soit, au cas où il importe, ici sont des problèmes sous-jacents wrt mise en œuvre de la gestion des sous-classes avec JSON, et sans langage de schéma (puisque JSON n'a pas vraiment largement utilisé un):

  • comment séparer les données (valeurs de propriété du bean) des métadonnées (informations de type nécessaires uniquement pour construire les sous-classes correctes) - doit être séparé, mais JSON comme le format n'a aucun moyen de définir (pourrait utiliser la convention de dénomination)
  • comment ajouter des annotations correctes générer et utiliser de telles métadonnées; et sans dépendre des caractéristiques spécifiques du langage (ne devrait pas avoir à lier aux noms de classes java par exemple)

Ce sont des problèmes résolus, mais pas trivialement facile à résoudre. :-)

+0

Au moins ceci une confirmation qu'il n'y a aucun moyen de le faire dans Jackson (encore), que je ne manque pas quelque chose; cela doit être fait à la dure. – seansand

+0

Correct - Je ne peux pas penser à un moyen de le faire de manière facile (sans sérialiseur/désérialiseur personnalisé) avec Jackson 1.2 ou avant. – StaxMan

+0

FWIW, Jackson version 1.5 aura un support pour la gestion complète du polymorphisme pour la désérialisation (le 1.4 vient d'être publié). – StaxMan

Questions connexes