Domanda

Ho un oggetto albero in formato JSON, sto cercando di deserializzare con GSON. Ogni nodo contiene i suoi nodi figlio come campi del nodo di tipo oggetto. Node è un'interfaccia, che ha diverse implementazioni di classe concreta. Durante il processo di deserializzazione, come posso comunicare a GSON quale classe concreta implementare durante la deseriazione del nodo, se non conosco a priori a quale tipo appartiene il nodo? Ogni nodo ha un campo membro che specifica il tipo. C'è un modo per accedere al campo quando l'oggetto è in forma serializzata e in qualche modo comunica il tipo a GSON?

Grazie!

È stato utile?

Soluzione

Suggerirei di aggiungere un'usanza Jsondeserializer per NodeS:

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

Sarai in grado di accedere al JsonElement Rappresentando il nodo nel metodo del deserializzatore, convertilo in a JsonObject, e recupera il campo che specifica il tipo. È quindi possibile creare un'istanza del tipo corretto di Node basato su questo.

Altri suggerimenti

Dovrai registrare sia JSonserializer che JSondeserializer. Inoltre puoi implementare un adattatore generico per tutte le tue interfacce nel modo seguente:

  • Durante la serializzazione: aggiungere un meta-info del tipo di classe implicativo effettivo.
  • Durante la deserializzazione: recuperare quel meta informativo e chiamare il jsondeseralize di quella classe

Ecco l'implementazione che ho usato per me e funziona bene.

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

}

Quindi potresti registrare questo adattatore per tutte le tue interfacce come segue

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

Per quanto posso dire, questo non funziona per i tipi di non collezione, o più specificamente, situazioni in cui il tipo di calcestruzzo viene utilizzato per serializzare e il tipo di interfaccia viene utilizzato per deserializzare. Cioè, se hai una semplice classe che implementa un'interfaccia e seriali la classe concreta, quindi specifica l'interfaccia per deserializzare, finirai in una situazione irrecuperabile.

Nell'esempio sopra l'adattatore di tipo è registrato rispetto all'interfaccia, ma quando si seria utilizzando la classe concreta non verrà utilizzato, il che significa che i dati Class_Meta_key non verranno mai impostati.

Se si specifica l'adattatore come adattatore gerarchico (dicendo così a GSON di usarlo per tutti i tipi nella gerarchia), finirai in un ciclo infinito poiché il serializzatore continuerà a chiamare se stesso.

Qualcuno sa come serializzare da un'implementazione concreta di un'interfaccia, quindi deserializza usando solo l'interfaccia e un incantatore?

Per impostazione predefinita sembra che GSON creerà l'istanza concreta, ma non imposterà i suoi campi.

Il problema è registrato qui:

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

Devi usare TypeToken Classe di Google GSON. Avrai bisogno ovviamente che abbia una classe T generica per farlo funzionare

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

gson.tojson (foo, sitype);

gson.fromJson(json, fooType);
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top