2010-09-02 7 views
32

J'ai un objet arbre au format JSON que j'essaye de désérialiser avec Gson. Chaque nœud contient ses nœuds enfants en tant que champs de type d'objet Node. Node est une interface qui comporte plusieurs implémentations de classes concrètes. Pendant le processus de désérialisation, comment puis-je communiquer à Gson la classe concrète à implémenter lors de la désérialisation du nœud, si je ne sais pas à priori à quel type appartient le nœud? Chaque nœud a un champ membre spécifiant le type. Y a-t-il un moyen d'accéder au champ lorsque l'objet est sous forme sérialisée, et de communiquer en quelque sorte le type à Gson?Désérialiser une classe abstraite dans Gson

Merci!

+0

Jetez un oeil à [http://stackoverflow.com/a/22081826/3315914](http://stackoverflow.com/a/22081826/3315914) – rpax

Répondre

40

Je vous suggère d'ajouter une coutume JsonDeserializer pour Node s:

Gson gson = new GsonBuilder() 
    .registerTypeAdapter(Node.class, new NodeDeserializer()) 
    .create(); 

Vous pourrez accéder au JsonElement représentant le noeud dans la méthode du désérialiseur, convertir en un JsonObject, et récupérer le champ spécifie le type. Vous pouvez ensuite créer une instance du type correct de Node en fonction de cela.

+0

Cela a parfaitement fonctionné. Merci! – user437480

+0

@ eng-carlin Il est d'usage d'upvote et d'accepter une réponse si cela fonctionne pour vous, donc si vous pouviez faire cela, je l'apprécierais. – ColinD

+0

La coche verte a été cliquée. Je vous avais upvote, mais ma réputation n'est pas assez élevée :( – user437480

22

Vous devez enregistrer JSONSerializer et JSONDeserializer. Vous pouvez également implémenter un adaptateur générique pour toutes vos interfaces de la façon suivante:

  • Pendant la sérialisation: ajoutez une info META du type de classe impl actuel.
  • Au cours de désérialisation: récupérera cette méta information et appelez la JSONDeserailize de cette classe

Voici la mise en œuvre que je l'ai utilisé pour moi-même et fonctionne très bien.

public class PropertyBasedInterfaceMarshal implements 
     JsonSerializer<Object>, JsonDeserializer<Object> { 

    private static final String CLASS_META_KEY = "CLASS_META_KEY"; 

    @Override 
    public Object deserialize(JsonElement jsonElement, Type type, 
      JsonDeserializationContext jsonDeserializationContext) 
      throws JsonParseException { 
     JsonObject jsonObj = jsonElement.getAsJsonObject(); 
     String className = jsonObj.get(CLASS_META_KEY).getAsString(); 
     try { 
      Class<?> clz = Class.forName(className); 
      return jsonDeserializationContext.deserialize(jsonElement, clz); 
     } catch (ClassNotFoundException e) { 
      throw new JsonParseException(e); 
     } 
    } 

    @Override 
    public JsonElement serialize(Object object, Type type, 
      JsonSerializationContext jsonSerializationContext) { 
     JsonElement jsonEle = jsonSerializationContext.serialize(object, object.getClass()); 
     jsonEle.getAsJsonObject().addProperty(CLASS_META_KEY, 
       object.getClass().getCanonicalName()); 
     return jsonEle; 
    } 

} 

Ensuite, vous pouvez enregistrer cet adaptateur pour toutes vos interfaces comme suit

Gson gson = new GsonBuilder() 
     .registerTypeAdapter(IInterfaceOne.class, 
       new PropertyBasedInterfaceMarshal()) 
     .registerTypeAdapter(IInterfaceTwo.class, 
       new PropertyBasedInterfaceMarshal()).create(); 
+0

Que voulez-vous dire par IInterfaceOne.class et IInterfaceTwo.class –

+0

Merci de fournir la solution la plus élégante/réutilisable à ce problème. J'utilise '_class' comme' CLASS_META_KEY' comme le fait mongodb. – bekce

1

Pour autant que je peux dire cela ne fonctionne pas pour les types de non-recouvrement, ou plus précisément, les situations où les Le type concret est utilisé pour sérialiser, et le type d'interface est utilisé pour désérialiser. Autrement dit, si vous avez une classe simple implémentant une interface et sérialisant la classe concrète, puis spécifiez l'interface à désérialiser, vous vous retrouverez dans une situation irrécupérable.

Dans l'exemple ci-dessus, l'adaptateur de type est enregistré sur l'interface, mais lorsque vous le sérialisez avec la classe concrète, il ne sera pas utilisé, ce qui signifie que les données CLASS_META_KEY ne seront jamais définies. Si vous spécifiez l'adaptateur en tant qu'adaptateur hiérarchique (indiquant ainsi à gson de l'utiliser pour tous les types dans la hiérarchie), vous vous retrouverez dans une boucle infinie car le sérialiseur continuera de s'appeler lui-même. Quelqu'un sait comment sérialiser à partir d'une implémentation concrète d'une interface, puis désérialiser en utilisant uniquement l'interface et un InstanceCreator?

Par défaut, il semble que gson créera l'instance concrète, mais ne définit pas ses champs.

problème est identifié:

http://code.google.com/p/google-gson/issues/detail?id=411&q=interface

0

Vous devez utiliser la classe TypeToken de Google Gson. Vous aurez besoin de cours a une classe T générique pour le rendre fonctionne

Type fooType = new TypeToken<Foo<Bar>>() {}.getType(); 

gson.toJson (foo, fooType);

gson.fromJson(json, fooType); 
Questions connexes