it-swarm.com.de

Deserialisierung generischer Typen mit GSON

Ich habe Probleme mit der Implementierung von Json Deserialization in meiner Android-Anwendung (mit der Gson-Bibliothek).

Ich habe Klasse so gemacht

public class MyJson<T>{
    public List<T> posts;
}

Und Deserialisierungsaufruf lautet:

public class JsonDownloader<T> extends AsyncTask<Void, Void, MyJson<T>> {
...
protected MyJson<T> doInBackground(Void... params) {
  ...
    Reader reader = new InputStreamReader(content);
    GsonBuilder gson = new GsonBuilder();
    Type collectionType = new TypeToken<MyJson<T>>() {}.getType();
    result = gson.create().fromJson(reader, collectionType);
  ...
  }
}

Das Problem ist, dass result.posts list after call ein Array von LinkedTreeMap-Objekten (mit korrekten Werten, also Deserialisierung des Problems) anstelle von MyJson-Objekten enthält. Wenn ich MyObject anstelle von T verwende, läuft alles einwandfrei und MyObject ist korrekt.

Gibt es eine Möglichkeit, einen Deserialisierungsaufruf zu implementieren, ohne einen benutzerdefinierten Deserialisierer zu erstellen?

39
VizGhar

Sie müssen den Typ von T zum Zeitpunkt der Deserialisierung angeben. Wie würde Ihr List von posts erstellt, wenn Gson nicht wüsste, welches Type zu instanziieren ist? Es kann nicht für immer T bleiben. Sie würden also den Typ T als Class -Parameter angeben.

Angenommen, der Typ von posts war String, als den Sie MyJson<String> Deserialisieren würden (der Einfachheit halber habe ich auch einen String json - Parameter hinzugefügt; Sie würden aus lesen Ihr reader wie zuvor):

doInBackground(String.class, "{posts: [\"article 1\", \"article 2\"]}");

protected MyJson<T> doInBackground(Class<T> type, String json, Void... params) {

    GsonBuilder gson = new GsonBuilder();
    Type collectionType = new TypeToken<MyJson<T>>(){}.getType();

    MyJson<T> myJson = gson.create().fromJson(json, collectionType);

    System.out.println(myJson.getPosts()); // ["article 1", "article 2"]
    return myJson;
}

Ebenso zum Deserialisieren eines MyJson von Boolean Objekten

doInBackground(Boolean.class, "{posts: [true, false]}");

protected MyJson<T> doInBackground(Class<T> type, String json, Void... params) {

    GsonBuilder gson = new GsonBuilder();
    Type collectionType = new TypeToken<MyJson<T>>(){}.getType();

    MyJson<T> myJson = gson.create().fromJson(json, collectionType);

    System.out.println(myJson.getPosts()); // [true, false]
    return myJson;
}

Ich habe MyJson<T> Für meine Beispiele als angenommen

public class MyJson<T> {

    public List<T> posts;

    public List<T> getPosts() {
        return posts;
    }
}

Wenn Sie also nach einer Deserialisierung von List<MyObject> Suchen, rufen Sie die Methode als auf

// assuming no Void parameters were required
MyJson<MyObject> myJson = doInBackground(MyObject.class);
30
Ravi Thapliyal

Hast du es versucht?

gson.create().fromJson(reader, MyJson.class);

EDIT

Nach dem Lesen von this post scheint die Verwendung von Type richtig zu sein. Ich glaube, Ihr Problem ist die Verwendung von T. Sie müssen sich daran erinnern, dass es bei Java eine Typlöschung gibt. Das bedeutet, dass zur Laufzeit alle Instanzen von T durch Object ersetzt werden. Zur Laufzeit ist also GSON wirklich MyJson<Object>. Wenn Sie dies mit einer konkreten Klasse anstelle von <T> versucht haben, glaube ich, dass es funktionieren würde.

Google Gson - Liste <Klasse> -Objekt deserialisieren? (generischer Typ)

24
John B

Die obige Antwort funktionierte also nicht für mich. Nach dem Ausprobieren endete mein Code:

public class AbstractListResponse<T> {
    private List<T> result;

    public List<T> getResult() {
        return this.result;
    }
}

Der wichtige Teil hier ist die Methodensignatur, einschließlich des '<T>' auf der linken Seite.

protected <T> AbstractListResponse<T> parseAbstractResponse(String json, TypeToken type) {
    return new GsonBuilder()
            .create()
            .fromJson(json, type.getType());
}

Beim Aufruf von Gson erhält die Methode das TypeToken des generischen Objekts.

TypeToken<AbstractListResponse<MyDTO>> typeToken = new TypeToken<AbstractListResponse<MyDTO>>() {};
AbstractListResponse<MyDTO> responseBase = parseAbstractResponse(json, typeToken);

Und schließlich kann das TypeToken MyDTO verwenden, oder sogar ein einfaches Objekt, nur MyDTO.

3
Davi Alves

Für jeden, der wie ich mit Kotlin kämpft, habe ich diesen Weg gefunden

val type = object : TypeToken<MyJson<MyObject>>() { }.type
val response = gson.fromJson<MyJson<MyObject>>(reader, type)

Beachten Sie, dass beim Aufruf einer generischen Funktion die Typargumente an der Aufrufstelle nach dem Namen der Funktion erforderlich sind (gesehen hier ).

0
tudor