Utiliser Gson et Retrofit 2 pour désérialiser les réponses d'API complexes
Je suis en utilisant Retrofit 2
et Gson
et je vais avoir de la difficulté à la désérialisation des réponses de mon API. Voici mon scénario:
J'ai un modèle objet nommé Employee
qui a trois champs: id
name
age
.
J'ai une API qui renvoie un singulier Employee
objet comme ceci:
{
"status": "success",
"code": 200,
"data": {
"id": "123",
"id_to_name": {
"123" : "John Doe"
},
"id_to_age": {
"123" : 30
}
}
}
Et une liste de Employee
objets comme ceci:
{
"status": "success",
"code": 200,
"data": [
{
"id": "123",
"id_to_name": {
"123" : "John Doe"
},
"id_to_age": {
"123" : 30
}
},
{
"id": "456",
"id_to_name": {
"456" : "Jane Smith"
},
"id_to_age": {
"456" : 35
}
},
]
}
Il y a trois choses à considérer ici:
- API réponses de retour dans un wrapper générique, à la part importante à l'intérieur de la
data
champ. - L'API retourne des objets dans un format qui n'est pas directement correspondent aux champs du modèle (par exemple, la valeur prise dans
id_to_age
doit être mappé à laage
champ sur le modèle) - La
data
champ dans l'API de réponse peut être un objet singulier, ou une liste d'objets.
Comment puis-je mettre en œuvre la désérialisation avec Gson
tel qu'il gère ces trois cas élégamment?
Idéalement, je préfère le faire entièrement avec TypeAdapter
ou TypeAdapterFactory
au lieu de payer l'amende de la performance de JsonDeserializer
. En fin de compte, j'ai envie de finir avec une instance de Employee
ou List<Employee>
tel qu'il répond à cette interface:
public interface EmployeeService {
@GET("/v1/employees/{employee_id}")
Observable<Employee> getEmployee(@Path("employee_id") String employeeId);
@GET("/v1/employees")
Observable<List<Employee>> getEmployees();
}
Cette question précédente, j'ai posté discute de ma première tentative à cet égard, mais il ne parvient pas à envisager de quelques-uns des problèmes évoqués ci-dessus:
À l'aide de Rénovation et de RxJava, comment puis-je désérialiser JSON quand il n'est pas directement un objet de modèle?
source d'informationauteur user2393462435
Vous devez vous connecter pour publier un commentaire.
EDIT: de mise à jour: la création d'un convertisseur personnalisé de l'usine NE fonctionne--la clé pour éviter une boucle infinie à travers
ApiResponseConverterFactory
'est l'appel de Rénovation dunextResponseBodyConverter
qui vous permet de spécifier une usine de sauter par-dessus. La clé est-ce serait unConverter.Factory
de s'inscrire auprès de Rénovation, pas unTypeAdapterFactory
pour Gson. Ce serait en fait préférable car elle évite le double-désérialisation de l'ResponseBody (pas besoin de désérialiser le corps puis recréer à nouveau comme une autre réponse).Voir le résumé ici pour un exemple de mise en œuvre.
RÉPONSE ORIGINALE À CETTE QUESTION:
La
ApiResponseAdapterFactory
approche ne fonctionne pas, sauf si vous êtes prêt à l'emballage de tous vos interfaces de service avecApiResponse<T>
. Cependant, il ya une autre option: OkHttp intercepteurs.Voici notre stratégie:
Response
Response#body()
sera désérialisé comme unApiResponse
et nous retourner une nouvelleResponse
où laResponseBody
est juste le contenu que l'on souhaite.Donc
ApiResponse
ressemble:ApiResponseInterceptor:
Configurer votre OkHttp et Rénovation:
Et
Employee
etEmployeeResponse
devraient suivre la fabrique de construire je l'ai écrit dans la question précédente. Maintenant, toutes lesApiResponse
champs doivent être consommés par l'intercepteur et tous les de Rénovation appel que vous faites doit renvoyer uniquement le contenu JSON qui vous intéresse.Je conseille un
JsonDeserializer
car il n'y a pas beaucoup de niveaux d'imbrication dans la réponse, donc il ne sera pas un gros gain de performance.Classes ressemblerait à quelque chose comme ceci:
Interface de Service doit être ajusté pour le générique de réponse:
C'est un générique de données de réponse:
Employé modèle:
Employé deserializer:
Le problème avec la réponse, c'est que
name
etage
sont contenus à l'intérieur d'un objet JSON qui se traduit par une Carte en Java il ne nécessite un peu plus de travail pour l'analyser.Il suffit de créer suivantes TypeAdapterFactory.
}
et l'ajouter dans votre GSON constructeur :
ou