Ich verwende eine API aus meiner Android-App und alle JSON-Antworten lauten wie folgt:
{
'status': 'OK',
'reason': 'Everything was fine',
'content': {
< some data here >
}
Das Problem ist, dass alle meine POJOs ein status
, reason
Felder haben und innerhalb des content
Feldes das echte POJO ist, das ich möchte.
Gibt es eine Möglichkeit, einen benutzerdefinierten Konverter von Gson zu erstellen, um immer das content
Feld zu extrahieren , sodass die Nachrüstung das entsprechende POJO zurückgibt?
Antworten:
Sie würden einen benutzerdefinierten Deserializer schreiben, der das eingebettete Objekt zurückgibt.
Angenommen, Ihr JSON lautet:
Sie hätten dann ein
Content
POJO:Dann schreiben Sie einen Deserializer:
Wenn Sie nun einen
Gson
mitGsonBuilder
erstellen und den Deserializer registrieren:Sie können Ihren JSON direkt auf Folgendes deserialisieren
Content
:Bearbeiten, um aus Kommentaren hinzuzufügen:
Wenn Sie verschiedene Arten von Nachrichten haben, aber alle das Feld "Inhalt" haben, können Sie den Deserializer generisch machen, indem Sie Folgendes tun:
Sie müssen nur eine Instanz für jeden Ihrer Typen registrieren:
Wenn Sie
.fromJson()
den Typ aufrufen, wird er in den Deserializer übertragen, sodass er für alle Ihre Typen funktionieren sollte.Und schließlich beim Erstellen einer Retrofit-Instanz:
quelle
setConverter(new GsonConverter(gson))
in RetrofitsRestAdapter.Builder
KlassePerson.class
undList<Person>.class
/ oderPerson[].class
mit getrenntem Deserializer?@ BrianRoachs Lösung ist die richtige Lösung. Es ist erwähnenswert, dass Sie in dem speziellen Fall, in dem Sie verschachtelte benutzerdefinierte Objekte haben, die beide eine benutzerdefinierte benötigen
TypeAdapter
, dieTypeAdapter
bei der neuen Instanz von GSON registrieren müssen , da sonst die zweiteTypeAdapter
niemals aufgerufen wird. Dies liegt daran, dass wir eine neueGson
Instanz in unserem benutzerdefinierten Deserializer erstellen .Zum Beispiel, wenn Sie den folgenden json hatten:
Und Sie wollten, dass dieser JSON den folgenden Objekten zugeordnet wird:
Sie müssten die
SubContent
's registrierenTypeAdapter
. Um robuster zu sein, können Sie Folgendes tun:und dann erstelle es so:
Dies kann leicht auch für den verschachtelten "Inhalts" -Fall verwendet werden, indem einfach eine neue Instanz von
MyDeserializer
mit Nullwerten übergeben wird.quelle
java.lang.reflect.Type
Etwas spät, aber hoffentlich hilft das jemandem.
Erstellen Sie einfach die folgende TypeAdapterFactory.
und fügen Sie es Ihrem GSON-Builder hinzu:
oder
quelle
jsonElement
? wie ich habecontent
,content1
etc.Hatte vor ein paar Tagen das gleiche Problem. Ich habe dieses Problem mit der Response-Wrapper-Klasse und dem RxJava-Transformator gelöst, was meiner Meinung nach eine recht flexible Lösung ist:
Verpackung:
Benutzerdefinierte Ausnahme zum Auslösen, wenn der Status nicht OK ist:
Empfangstransformator:
Anwendungsbeispiel:
Mein Thema: Retrofit 2 RxJava - Gson - "Globale" Deserialisierung, Antworttyp ändern
quelle
In Fortsetzung von Brians Idee könnte es nützlich sein, die Deserialisierung zu verallgemeinern, da wir fast immer viele REST-Ressourcen mit jeweils eigener Wurzel haben:
Um dann die Beispielnutzlast von oben zu analysieren, können wir den GSON-Deserializer registrieren:
quelle
Eine bessere Lösung könnte dies sein ..
Definieren Sie dann Ihren Service wie folgt.
quelle
Gemäß der Antwort von @Brian Roach und @rafakob habe ich dies folgendermaßen gemacht
Json Antwort vom Server
Gemeinsame Datenhandlerklasse
Benutzerdefinierter Serializer
Gson-Objekt
API-Anruf
quelle
Dies ist die gleiche Lösung wie bei @AYarulin, es wird jedoch davon ausgegangen, dass der Klassenname der JSON-Schlüsselname ist. Auf diese Weise müssen Sie nur den Klassennamen übergeben.
Um dann die Beispielnutzlast von oben zu analysieren, können wir den GSON-Deserializer registrieren. Dies ist problematisch, da bei dem Schlüssel zwischen Groß- und Kleinschreibung unterschieden wird. Daher muss die Groß- und Kleinschreibung des Klassennamens mit der Groß- und Kleinschreibung des JSON-Schlüssels übereinstimmen.
quelle
Hier ist eine Kotlin-Version, die auf den Antworten von Brian Roach und AYarulin basiert.
quelle
In meinem Fall würde sich der "Inhalts" -Schlüssel für jede Antwort ändern. Beispiel:
In solchen Fällen habe ich eine ähnliche Lösung wie oben aufgeführt verwendet, musste sie jedoch optimieren. Sie können das Wesentliche hier sehen . Es ist etwas zu groß, um es hier auf SOF zu veröffentlichen.
Die Anmerkung
@InnerKey("content")
wird verwendet und der Rest des Codes soll die Verwendung mit Gson erleichtern.quelle
Vergessen Sie nicht
@SerializedName
und@Expose
Anmerkungen für alle Klassenmitglieder und Mitglieder der Inneren Klasse, die von GSON am meisten von JSON deserialisiert wurden.Schauen Sie sich https://stackoverflow.com/a/40239512/1676736 an
quelle
Eine weitere einfache Lösung:
quelle