Je consomme une API de mon application Android, et toutes les réponses JSON sont comme ceci:
{
'status': 'OK',
'reason': 'Everything was fine',
'content': {
< some data here >
}
Le problème est que tous mes POJO ont un status
, les reason
champs, et à l' intérieur du content
champ est le vrai POJO je veux.
Existe-t-il un moyen de créer un convertisseur personnalisé de Gson pour extraire toujours le content
champ, de sorte que la mise à niveau renvoie le POJO approprié?
Réponses:
Vous écririez un désérialiseur personnalisé qui renvoie l'objet incorporé.
Disons que votre JSON est:
Vous auriez alors un
Content
POJO:Ensuite, vous écrivez un désérialiseur:
Maintenant, si vous construisez un
Gson
withGsonBuilder
et enregistrez le désérialiseur:Vous pouvez désérialiser votre JSON directement dans votre
Content
:Modifier pour ajouter à partir des commentaires:
Si vous avez différents types de messages mais qu'ils ont tous le champ «contenu», vous pouvez rendre le désérialiseur générique en faisant:
Il vous suffit d'enregistrer une instance pour chacun de vos types:
Lorsque vous appelez,
.fromJson()
le type est transporté dans le désérialiseur, il devrait donc fonctionner pour tous vos types.Et enfin lors de la création d'une instance Retrofit:
la source
setConverter(new GsonConverter(gson))
dans laRestAdapter.Builder
classe RetrofitPerson.class
etList<Person>.class
/Person[].class
avec désérialiseur séparé?La solution de @ BrianRoach est la bonne solution. Il est à noter que dans le cas particulier où vous avez des objets personnalisés imbriqués qui ont tous deux besoin d'une personnalisation
TypeAdapter
, vous devez enregistrer leTypeAdapter
avec la nouvelle instance de GSON , sinon la secondeTypeAdapter
ne sera jamais appelée. C'est parce que nous créons une nouvelleGson
instance dans notre désérialiseur personnalisé.Par exemple, si vous aviez le json suivant:
Et vous vouliez que ce JSON soit mappé aux objets suivants:
Vous devrez enregistrer les
SubContent
fichiersTypeAdapter
. Pour être plus robuste, vous pouvez effectuer les opérations suivantes:puis créez-le comme ceci:
Cela pourrait facilement être utilisé pour le cas "contenu" imbriqué en passant simplement une nouvelle instance de
MyDeserializer
avec des valeurs nulles.la source
java.lang.reflect.Type
Un peu tard mais j'espère que cela aidera quelqu'un.
Créez simplement TypeAdapterFactory suivant.
et ajoutez-le dans votre générateur GSON:
ou
la source
jsonElement
?. comme je l'aicontent
,content1
etc.J'ai eu le même problème il y a quelques jours. J'ai résolu cela en utilisant la classe de wrapper de réponse et le transformateur RxJava, ce qui, à mon avis, est une solution assez flexible:
Emballage:
Exception personnalisée à lever, lorsque l'état n'est pas OK:
Transformateur Rx:
Exemple d'utilisation:
Mon sujet: Retrofit 2 RxJava - Gson - Désérialisation "globale", changement de type de réponse
la source
Poursuivant l'idée de Brian, car nous avons presque toujours de nombreuses ressources REST, chacune avec sa propre racine, il pourrait être utile de généraliser la désérialisation:
Ensuite, pour analyser l'exemple de charge utile ci-dessus, nous pouvons enregistrer le désérialiseur GSON:
la source
Une meilleure solution pourrait être celle-ci.
Ensuite, définissez votre service comme ceci.
la source
Selon la réponse de @Brian Roach et @rafakob, je l'ai fait de la manière suivante
Réponse Json du serveur
Classe de gestionnaire de données commune
Sérialiseur personnalisé
Objet Gson
Appel API
la source
C'est la même solution que @AYarulin mais supposons que le nom de classe est le nom de la clé JSON. De cette façon, il vous suffit de transmettre le nom de la classe.
Ensuite, pour analyser l'exemple de charge utile ci-dessus, nous pouvons enregistrer le désérialiseur GSON. Ceci est problématique car la clé est sensible à la casse, donc la casse du nom de classe doit correspondre à la casse de la clé JSON.
la source
Voici une version Kotlin basée sur les réponses de Brian Roach et AYarulin.
la source
Dans mon cas, la clé "contenu" changerait pour chaque réponse. Exemple:
Dans de tels cas, j'ai utilisé une solution similaire à celle indiquée ci-dessus, mais j'ai dû la modifier. Vous pouvez voir l'essentiel ici . C'est un peu trop grand pour le publier ici sur SOF.
L'annotation
@InnerKey("content")
est utilisée et le reste du code sert à faciliter son utilisation avec Gson.la source
Ne pas oublier
@SerializedName
et@Expose
annotations pour tous les membres de classe et membres de classe interne que la plupart désérialisés de JSON par GSON.Regardez https://stackoverflow.com/a/40239512/1676736
la source
Une autre solution simple:
la source