web-dev-qa-db-fra.com

Comment gérer le JSON dynamique dans la mise à niveau?

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 responseMessagequi change en objectname__, de manière aléatoire, le même préfixe (responseMessagename__) 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"
   }

}

responseMessageLe 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 JSONname__, nous devons affecter un seul POJO par demande! mais l'API REST est malheureusement construite avec des réponses dynamiques JSONname__, 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 JSONdans 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 JSONname__! 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 JSONname__!

Solution possible que je connais

  1. Utilisez l'ancienne tâche asyc avec l'analyse du client http. :(

  2. Essayez de convaincre le développeur backend de RESTapi.

  3. Créer un client de modification personnalisé :)

68
LOG_TAG

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.

35
Oliver Hausler

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".

17
Yuki Yoshida

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

10
Navneet Krishna

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.

10
Justin Slade

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é.

8
meda

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 :).

6
Lunchbox

S'il n'était pas possible de changer l'API backend, je considérerais les variantes suivantes (si Gson est utilisé pour convertir JSON).

  1. 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)).

  2. 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.

3
Roman Mazur

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

1
Sugesh

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.

0
Akhil T Mohan