Comment gérer Dynamic JSON en Retrofit?

j'utilise la mise à niveau efficace de la bibliothèque de réseau, mais j'ai échoué à gérer dynamique JSON qui contient le préfixe simple responseMessage qui change à object au hasard, le même préfixe ( responseMessage ) change à chaîne dans certains cas (dynamiquement).

JSON format objet de responsabilité:

{
   "applicationType":"1",
   "responseMessage":{
      "surname":"Jhon",
      "forename":" taylor",
      "dob":"17081990",
      "refNo":"3394909238490F",
      "result":"Received"
   }

}

responseMessage le format Json change dynamiquement en chaîne de caractères:

 {
       "applicationType":"4",
       "responseMessage":"Success"          
 }

le le problème pour moi est que depuis retrofit a intégré JSON parsing nous devons assigner un POJO simple par demande! mais le reste-API est malheureusement construit avec des réponses dynamiques JSON , le préfixe va changer à string pour objecter au hasard dans les deux succès (...) et l'é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.préfixe java responsiemessage est défini à chaîne ou objet de type responsiemessage, de sorte que nous pouvons créer le POJO avec la variable ref avec le même nom (Java basics :) ) donc je suis à la recherche de la même solution pour dynamique JSON en Retrofit. Je sais que c'est un travail très facile dans les clients http normaux avec la tâche async, mais ce n'est pas la meilleure pratique dans le reste-Api JSON parsing! en regardant la performance Benchmarks toujours Volley ou Retrofit est le meilleur choix, mais je n'ai pas réussi à manier dynamic JSON !

solution Possible je Sais

  1. Utiliser l'ancien asyc tâche avec un client http de l'analyse. : (

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

  3. Créer des Rénovations client :)

64
demandé sur hackjutsu 2014-06-18 11:15:24

9 réponses

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 met en œuvre 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 ce convertisseur d'échantillons pour qu'il renvoie la réponse de Json sous forme de chaîne, objet, JsonObject ou Map< String, objet >. Évidemment, tous les types de retour ne fonctionneront pas pour chaque Json, et il y a certainement place à amélioration. Mais il montre comment utiliser un Convertisseur pour convertir presque toutes les réponses à dynamic Json.

31
répondu Oliver Hausler 2015-05-20 11:13:30

RestClient.java

import retrofit.client.Response;
public interface RestClient {
  @GET("/api/foo") Response getYourJson();
}

votre classe.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" checkresponemessage".

16
répondu Yuki Yoshida 2014-07-01 13:11:34

N'importe laquelle de vos solutions possibles fonctionnera. Vous pouvez également envoyer le type de retour de L'interface api Retrofit à response. Avec cette réponse, vous obtenez un corps Inputstream que vous pouvez convertir en un objet JSON et lire comme bon vous semble.

regardez: http://square.github.io/retrofit/#api-declaration - sous réponse Type d'objet

mise à Jour

Rénovation 2 est maintenant et avec quelques changements à la documentation et à la bibliothèque.

regardez http://square.github.io/retrofit/#restadapter-configuration il y a un objet request and response body qui peut être utilisé.

10
répondu Justin Slade 2016-12-09 13:17:08

la réponse acceptée semblait trop compliquée pour moi, je la résous 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());
    }
});

ce n'est qu'un exemple pour essayer de vous montrer comment vous pouvez utiliser un modèle différent.

la variable isEmail est juste un booléen pour votre condition d'utiliser le modèle approprié.

6
répondu meda 2016-02-26 18:25:41

je sais que je suis très en retard à la fête. J'avais un problème similaire et je l'ai 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 changé de type pour objet. J'ai choisi cette approche parce qu'un seul champ dans la réponse était dynamique (pour moi, ma réponse était beaucoup plus compliquée), donc l'utilisation d'un convertisseur aurait rendu la vie difficile. Utilisé Gson pour travailler avec L'objet à partir de là, selon si c'était une chaîne de caractères ou une valeur de tableau.

espérons que aide à quelqu'un qui cherche une réponse simple :).

5
répondu Lunchbox 2016-10-19 14:42:30

Essayer personnalisé deserialisation à l'aide de gson-converter comme ci-dessous(mise à jour de la réponse de Rénovation 2.0)

créer trois modèles comme indiqué ci-dessous

Responsiewrapper

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;
    }

}

responsabilité

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;
}

}

Reponsestring

public class ResponseString extends ResponseWrapper {

}

Useresponsedeserializer (custom deserialiser)

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);
        }

}
}

Rénovation 2.0 Mise En Œuvre

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

compiler", com.squareup.adaptation 2: adaptation: 2.3.0'

compiler", com.squareup.retrofit2:convertisseur-gson:2.3.0'

***** précédent Réponse (la réponse ci-dessus est plus recommandée)) *****

changez votre pojo comme ceci

public class TrackerRefResponse {

  private String applicationType;
  private Object responseMessage;

  public Object getResponseMessage() {
  return responseMessage;
  }

  public void setResponseMessage(Object responseMessage) {
  this.responseMessage = responseMessage;
 }
}

et le changement de rénovation du onResponse comme ce

 @Override
public void onResponse(Response<TrackerRefResponse > response) {
    if (response.isSuccess()) {
        if (response.getResponseMessage() instanceof String)
            {
            handleStringResponse();
         }
        else 
            {
            handleObjectResponse();
         }
    }
}
5
répondu Navneet Krishna 2018-07-25 07:55:35

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 adaptateurs de type pour créer un adaptateur personnalisé pour ResponseMessage type qui décide dynamiquement comment analyser le JSON inoming (en utilisant quelque chose comme if (reader.peek() == JsonToken.STRING) ).

  2. Mettez des méta-données décrivant le type de réponse dans un en-tête HTTP et l'utiliser pour déterminer quel type d'information doit être transmis à l'instance Gson.

3
répondu Roman Mazur 2014-07-02 19:05:24

en plus de ce que vous avez dit -

Use Rappel Ensuite, vous pouvez récupérer les champs en utilisant la méthode get régulière. Pour plus d'informations, consultez le javadoc de gson.

http://google-gson.googlecode.com/svn/tags/1.2.3/docs/javadocs/com/google/gson/JsonObject.html

1
répondu Sugesh 2014-07-01 13:02:15

j'ai trop couru de cette question. mais je ne suis pas sûr que ce soit votre cas , (J'utilise Retrofit2)

sur mon cas, je dois gérer les messages d'erreur et de succès.

À 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"
        }
    ]
}
}

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 touche result de json contient des données de type data1, data2, alors le ValidateUserResult.java est initialisé. Si erreur, alors la classe Error.java est initialisée.

0
répondu Akhil T Mohan 2018-09-24 11:43:23