How to handle Dynamic JSON in Retrofit?

后端 未结 10 2229
余生分开走
余生分开走 2020-11-22 16:23

I am using the retrofit efficient networking library, but I am unable to handle Dynamic JSON which contains single prefix responseMessage which changes to

相关标签:
10条回答
  • 2020-11-22 16:51

    I know I am very very late to the party. I had a similar issue and just solved it like this:

    public class TrackerRefResponse {
    
        private String applicationType;
        // Changed to Object. Works fine with String and array responses.
        private Object responseMessage;
    
    }
    

    I literally just changed to type to Object. I chose this approach because only one field in the response was dynamic (for me, my response was way more complicated), so using a converter would have made life difficult. Used Gson to work with the Object from there, depending on if it was a String or Array value.

    Hope this helps someone looking for a simple answer :).

    0 讨论(0)
  • 2020-11-22 16:52

    The accepted answer seemed over complicated for me, I solve it this way:

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

    This is just an example in attempt to show you how you can use different model.

    The variable isEmail is just a boolean for your condition to use the appropriate model.

    0 讨论(0)
  • 2020-11-22 16:55

    In addition to what you told -

    Use Callback Then you can retrieve the fields using regular get method. For more information, go through the javadoc of gson.

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

    0 讨论(0)
  • 2020-11-22 16:55

    I too ran of this issue. but i am not sure if this was your case , (i am using Retrofit2)

    on my case i need to handle error, and success messages.

    On Success

    {
    "call_id": 1,
    "status": "SUCCESS",
    "status_code": "SUCCESS",
    "result": {
        "data1": {
            "id": "RFP2UjW7p8ggpMXzYO9tRg==",
            "name": "abcdef",
            "mobile_no": "96655222",
            "email": ""
        },
        "data2": [
            {
                "no": "12345"
            },
            {
                "no": "45632"
            }
        ]
    }
    }
    

    On Error,

    {
    "call_id": 1,
    "status": "FAILED",
    "status_code": "NO_RECORDS",
    "error": {
        "error_title": "xxx",
        "error_message": "details not found"
    }
    }
    

    for this i just created another 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;
    }
    

    in the above case if the result key on json contains data1,data2 then the ValidateUserResult.java get initialised. if error then the Error.java class get initialized.

    0 讨论(0)
  • 2020-11-22 16:56

    Try custom deserialisation using gson-converter as below(updated answer for Retrofit 2.0)

    Create three models as shown below

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

    Retrofit 2.0 Implementation

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

    Libraries Used

    compile 'com.squareup.retrofit2:retrofit:2.3.0'

    compile 'com.squareup.retrofit2:converter-gson:2.3.0'

    ***** Previous Answer (above answer is more recommended one) *****

    Change your pojo like this

    public class TrackerRefResponse {
    
      private String applicationType;
      private Object responseMessage;
    
      public Object getResponseMessage() {
      return responseMessage;
      }
    
      public void setResponseMessage(Object responseMessage) {
      this.responseMessage = responseMessage;
     }
    }
    

    and change retrofit's onResponse like this

     @Override
    public void onResponse(Response<TrackerRefResponse > response) {
        if (response.isSuccess()) {
            if (response.getResponseMessage() instanceof String)
                {
                handleStringResponse();
             }
            else 
                {
                handleObjectResponse();
             }
        }
    }
    

    you may also check this post for more details about dynamic json parsing

    0 讨论(0)
  • 2020-11-22 16:57

    If it was not possible to change the backend API, I would consider the following variants (if Gson is used to convert JSON).

    1. We can use Gson type adapters to create a custom adapter for ResponseMessage type that dynamically decides how to parse the inoming JSON (using something like if (reader.peek() == JsonToken.STRING)).

    2. Put some meta information describing the response type into an HTTP header and use it to determine what type information must be fed to Gson instance.

    0 讨论(0)
提交回复
热议问题