Question

J'ai un objet d'arbre au format JSON que j'essaie de désérialiser avec GSON. Chaque nœud contient ses nœuds enfants comme champs de nœud de type objet. Le nœud est une interface, qui a plusieurs implémentations de classe concrètes. Pendant le processus de désérialisation, comment puis-je communiquer à GSON quelle classe concrète à implémenter lors de la désérialisation du nœud, si je ne sais pas a priori à quel type le nœud appartient? Chaque nœud a un champ membre spécifiant le type. Existe-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?

Merci!

Était-ce utile?

La solution

Je suggère d'ajouter une coutume JSONSeRializer pour NodeS:

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

Vous pourrez accéder au JsonElement Représentant le nœud dans la méthode du désérialiseur, convertissez-le en un JsonObject, et récupérer le champ qui spécifie le type. Vous pouvez ensuite créer une instance du bon type de Node basé sur cela.

Autres conseils

Vous devrez enregistrer JSonSerializer et JSondeSerializer. Vous pouvez également implémenter un adaptateur générique pour toutes vos interfaces de la manière suivante:

  • Pendant la sérialisation: ajoutez un méta-info du type de classe IMP réel.
  • Pendant la désérialisation: récupèrez les méta-informations et appelez le jsonseserailize de cette classe

Voici l'implémentation que j'ai utilisée pour moi et fonctionne 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();

Pour autant que je sache, cela ne fonctionne pas pour les types de non-collection, ou plus précisément, les situations où le type de béton 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 que vous sérialisez la classe concrète, puis spécifiez l'interface pour désérialiser, vous vous retrouverez dans une situation irréprochable.

Dans l'exemple ci-dessus, l'adaptateur de type est enregistré par rapport à l'interface, mais lorsque vous sérialisez à l'aide de la classe de béton, 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 comme un adaptateur hiérarchique (disant 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.

Quelqu'un sait-il comment sérialiser à partir d'une implémentation concrète d'une interface, puis désérialiser en utilisant uniquement l'interface et un instanCreator?

Par défaut, il semble que GSON crée l'instance en béton, mais ne définit pas ses champs.

Le problème est enregistré ici:

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

Vous devez utiliser TypeToken Classe de Google GSON. Vous aurez bien sûr besoin d'avoir une classe T générique pour le faire fonctionner

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

gson.tojson (foo, joueur de pied);

gson.fromJson(json, fooType);
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top