J'utilise efficacement la modernisation de la bibliothèque réseau, mais je n'ai pas réussi à gérer le JSON dynamique, qui contient le préfixe unique responseMessage
qui change en object
name__, de manière aléatoire, le même préfixe (responseMessage
name__) passe à String dans certains cas (de manière dynamique).
Format Json Objet de responseMessage:
{
"applicationType":"1",
"responseMessage":{
"surname":"Jhon",
"forename":" taylor",
"dob":"17081990",
"refNo":"3394909238490F",
"result":"Received"
}
}
responseMessage
Le format Json change dynamiquement en type chaîne:
{
"applicationType":"4",
"responseMessage":"Success"
}
Le problème pour moi est que depuis que retrofit intègre l'analyse JSON
name__, nous devons affecter un seul POJO par demande! mais l'API REST est malheureusement construite avec des réponses dynamiques JSON
name__, le préfixe se transformant en chaîne en objet de manière aléatoire dans success (...) et échec (...) méthodes !
void doTrackRef(Map<String, String> paramsref2) {
RestAdapter restAdapter = new RestAdapter.Builder().setEndpoint("http://192.168.100.44/RestDemo").build();
TrackerRefRequest userref = restAdapter.create(TrackerRefRequest.class);
userref.login(paramsref2,
new Callback<TrackerRefResponse>() {
@Override
public void success(
TrackerRefResponse trackdetailresponse,
Response response) {
Toast.makeText(TrackerActivity.this, "Success",
Toast.LENGTH_SHORT).show();
}
@Override
public void failure(RetrofitError retrofitError) {
Toast.makeText(TrackerActivity.this, "No internet",
Toast.LENGTH_SHORT).show();
}
});
}
Pojo:
public class TrackerRefResponse {
private String applicationType;
private String responseMessage; //String type
//private ResponseMessage responseMessage; //Object of type ResponseMessage
//Setters and Getters
}
Dans le code ci-dessus, POJO TrackerRefResponse.Java, le préfixe responseMessage est défini sur une chaîne ou un objet de type responseMessage. Nous pouvons donc créer le POJO avec la variable ref portant le même nom (notions de base en Java :)). Je recherche donc la même solution dynamique pour JSON
dans Retrofit. . Je sais que c'est un travail très facile avec des clients HTTP normaux avec une tâche asynchrone, mais ce n'est pas la meilleure pratique dans l'analyse REST-Api JSON
name__! En regardant la performance Benchmarks Toujours Volley ou Retrofit est le meilleur choix, mais je n'ai pas réussi à gérer le comportement dynamique JSON
name__!
Solution possible que je connais
Utilisez l'ancienne tâche asyc avec l'analyse du client http. :(
Essayez de convaincre le développeur backend de RESTapi.
Créer un client de modification personnalisé :)
En retard à la fête, mais vous pouvez utiliser un convertisseur.
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("https://graph.facebook.com")
.setConverter(new DynamicJsonConverter()) // set your static class as converter here
.build();
api = restAdapter.create(FacebookApi.class);
Ensuite, vous utilisez une classe statique qui implémente le convertisseur de retrofit:
static class DynamicJsonConverter implements Converter {
@Override public Object fromBody(TypedInput typedInput, Type type) throws ConversionException {
try {
InputStream in = typedInput.in(); // convert the typedInput to String
String string = fromStream(in);
in.close(); // we are responsible to close the InputStream after use
if (String.class.equals(type)) {
return string;
} else {
return new Gson().fromJson(string, type); // convert to the supplied type, typically Object, JsonObject or Map<String, Object>
}
} catch (Exception e) { // a lot may happen here, whatever happens
throw new ConversionException(e); // wrap it into ConversionException so retrofit can process it
}
}
@Override public TypedOutput toBody(Object object) { // not required
return null;
}
private static String fromStream(InputStream in) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder out = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
out.append(line);
out.append("\r\n");
}
return out.toString();
}
}
J'ai écrit cet exemple de convertisseur pour qu'il retourne la réponse Json sous forme de chaîne, objet, JsonObject ou Map <chaîne, objet>. Évidemment, tous les types de retour ne fonctionneront pas pour chaque Json, et il y a certainement place à l'amélioration. Mais cela montre comment utiliser un convertisseur pour convertir presque toutes les réponses en Json dynamique.
RestClient.Java
import retrofit.client.Response;
public interface RestClient {
@GET("/api/foo") Response getYourJson();
}
YourClass.Java
RestClient restClient;
// create your restClient
Response response = restClient.getYourJson();
Gson gson = new Gson();
String json = response.getBody().toString();
if (checkResponseMessage(json)) {
Pojo1 pojo1 = gson.fromJson(json, Pojo1.class);
} else {
Pojo2 pojo2 = gson.fromJson(json, Pojo2.class);
}
Vous devez implémenter la méthode "checkResponseMessage".
Essayez une désérialisation personnalisée en utilisant gson-converter
comme ci-dessous (réponse mise à jour pour Retrofit 2.0)
Créez trois modèles comme indiqué ci-dessous
ResponseWrapper
public class ResponseWrapper {
@SerializedName("applicationType")
@Expose
private String applicationType;
@SerializedName("responseMessage")
@Expose
private Object responseMessage;
public String getApplicationType() {
return applicationType;
}
public void setApplicationType(String applicationType) {
this.applicationType = applicationType;
}
public Object getResponseMessage() {
return responseMessage;
}
public void setResponseMessage(Object responseMessage) {
this.responseMessage = responseMessage;
}
}
ResponseMessage
public class ResponseMessage extends ResponseWrapper {
@SerializedName("surname")
@Expose
private String surname;
@SerializedName("forename")
@Expose
private String forename;
@SerializedName("dob")
@Expose
private String dob;
@SerializedName("refNo")
@Expose
private String refNo;
@SerializedName("result")
@Expose
private String result;
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public String getForename() {
return forename;
}
public void setForename(String forename) {
this.forename = forename;
}
public String getDob() {
return dob;
}
public void setDob(String dob) {
this.dob = dob;
}
public String getRefNo() {
return refNo;
}
public void setRefNo(String refNo) {
this.refNo = refNo;
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
}
ResponseString
public class ResponseString extends ResponseWrapper {
}
UserResponseDeserializer (désérialiseur personnalisé)
public class UserResponseDeserializer implements JsonDeserializer<ResponseWrapper> {
@Override
public ResponseWrapper deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (((JsonObject) json).get("responseMessage") instanceof JsonObject){
return new Gson().fromJson(json, ResponseMessage.class);
} else {
return new Gson().fromJson(json, ResponseString.class);
}
}
}
Mise en œuvre de la version 2.0
Gson userDeserializer = new GsonBuilder().setLenient().registerTypeAdapter(ResponseWrapper.class, new UserResponseDeserializer()).create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("base_url")
.addConverterFactory(GsonConverterFactory.create(userDeserializer))
.build();
UserService request = retrofit.create(UserService.class);
Call<ResponseWrapper> call1=request.listAllUsers();
call1.enqueue(new Callback<ResponseWrapper>() {
@Override
public void onResponse(Call<ResponseWrapper> call, Response<ResponseWrapper> response) {
ResponseWrapper responseWrapper=response.body();
Log.i("DYNAMIC RESPONSE", String.valueOf(response.body().getResponseMessage()));
}
@Override
public void onFailure(Call<ResponseWrapper> call, Throwable t) {
}
});
Bibliothèques utilisées
compile 'com.squareup.retrofit2: modification: 2.3.0'
compile 'com.squareup.retrofit2: converter-gson: 2.3.0'
***** Réponse précédente (la réponse ci-dessus est plus recommandée) *****
Changez votre pojo comme ça
public class TrackerRefResponse {
private String applicationType;
private Object responseMessage;
public Object getResponseMessage() {
return responseMessage;
}
public void setResponseMessage(Object responseMessage) {
this.responseMessage = responseMessage;
}
}
et changer onResponse de retrofit comme ça
@Override
public void onResponse(Response<TrackerRefResponse > response) {
if (response.isSuccess()) {
if (response.getResponseMessage() instanceof String)
{
handleStringResponse();
}
else
{
handleObjectResponse();
}
}
}
vous pouvez aussi vérifier ce post pour plus de détails sur l'analyse json dynamique
Chacune de vos solutions possibles fonctionnera. Ce que vous pouvez également faire est d’envoyer le type de retour d’interface Retrofit api à la réponse. Avec cette réponse, vous obtenez un corps Inputstream
que vous pouvez convertir en objet JSON et lire à votre guise.
Consultez: http://square.github.io/retrofit/#api-declaration - sous TYPE D'OBJET DE RÉPONSE
Mis à jour
Retrofit 2 est disponible et apporte quelques modifications à la documentation et à la bibliothèque.
Regardez http://square.github.io/retrofit/#restadapter-configuration il existe des objets de corps de requête et de réponse pouvant être utilisés.
La réponse acceptée semblait trop compliquée pour moi, je la résout de cette façon:
Call<ResponseBody> call = client.request(params);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Response<ResponseBody> response) {
if (response.isSuccess()) {
Gson gson = new Gson();
ResponseBody repsonseBody = response.body().string();
if (isEmail) {
EmailReport reports = gson.fromJson(responseBody, EmailReport.class);
} else{
PhoneReport reports = gson.fromJson(repsonseBody, PhoneReport.class);
}
}
}
@Override
public void onFailure(Throwable t) {
Log.e(LOG_TAG, "message =" + t.getMessage());
}
});
Ceci est juste un exemple dans la tentative de vous montrer comment vous pouvez utiliser un modèle différent.
La variable isEmail
est simplement un booléen pour que votre condition utilise le modèle approprié.
Je sais que je suis très très en retard à la fête. J'ai eu un problème similaire et je l'ai simplement résolu comme ceci:
public class TrackerRefResponse {
private String applicationType;
// Changed to Object. Works fine with String and array responses.
private Object responseMessage;
}
J'ai littéralement changé pour taper à Object. J'ai choisi cette approche car un seul domaine de la réponse était dynamique (pour moi, ma réponse était bien plus compliquée), utiliser un convertisseur aurait donc rendu la vie difficile. Utilisé Gson pour travailler avec l’objet à partir de là, selon qu’il s’agissait d’une valeur String ou Array.
J'espère que cela aide quelqu'un qui cherche une réponse simple :).
S'il n'était pas possible de changer l'API backend, je considérerais les variantes suivantes (si Gson est utilisé pour convertir JSON).
Nous pouvons utiliser Gson type adapters pour créer un adaptateur personnalisé pour le type ResponseMessage
qui décide dynamiquement comment analyser le JSON entrant (en utilisant quelque chose comme if (reader.peek() == JsonToken.STRING)
).
Mettez des méta-informations décrivant le type de réponse dans un en-tête HTTP et utilisez-les pour déterminer les informations de type devant être fournies à l'instance Gson.
En plus de ce que vous avez dit -
Utilisez Callback Vous pouvez ensuite récupérer les champs à l’aide de la méthode get habituelle . Pour plus d’informations, reportez-vous au javadoc de gson.
http://google-gson.googlecode.com/svn/tags/1.2.3/docs/javadocs/com/google/gson/JsonObject.html
Moi aussi, je me suis occupé de ce problème ... mais je ne sais pas si c'était votre cas (j'utilise Retrofit2)
dans mon cas, j'ai besoin de gérer les messages d'erreur et de succès.
Sur le succès
{
"call_id": 1,
"status": "SUCCESS",
"status_code": "SUCCESS",
"result": {
"data1": {
"id": "RFP2UjW7p8ggpMXzYO9tRg==",
"name": "abcdef",
"mobile_no": "96655222",
"email": ""
},
"data2": [
{
"no": "12345"
},
{
"no": "45632"
}
]
}
}
En cas d'erreur,
{
"call_id": 1,
"status": "FAILED",
"status_code": "NO_RECORDS",
"error": {
"error_title": "xxx",
"error_message": "details not found"
}
}
pour cela je viens de créer un autre POJO Error
,
public class ValidateUserResponse {
@SerializedName("call_id")
public String callId;
@SerializedName("status")
public String status;
@SerializedName("status_code")
public String statusCode;
@SerializedName("result")
public ValidateUserResult result;
@SerializedName("error")
public Error error;
}
Error.Java
public class Error {
@SerializedName("error_title")
public String errorTitle;
@SerializedName("error_message")
public String errorMessage;
}
ValidateUser.Java
public class ValidateUserResult {
@SerializedName("auth_check")
public String authCheck;
@SerializedName("data1")
public Data1 data1;
@SerializedName("data2")
public List<Data2> data2;
}
dans le cas ci-dessus, si la clé result
de json contient data1, data2, le ValidateUserResult.Java
est initialisé . si une erreur survient, la classe Error.Java
est initialisée.