How to handle Dynamic JSON in Upgrades?

I am using an efficient network library upgrade, but I was not able to process Dynamic JSON, which contains one responseMessage prefix that accidentally changes to object , the same prefix ( responseMessage ) in some cases changes to String (dynamically).

Json format responseMessage Object:

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

responseMessage The Json format is dynamically changed to a string like:

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

The problem for me is that the modification has built-in JSON parsing, we must assign one POJO per request! but the REST-API, unfortunately, is built with dynamic JSON responses, the prefix will randomly change to a string per object in both Success (...) and failure (...) !

 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 } 

In the POJO TrackerRefResponse.java code above, the responseMessage prefix is ​​set to a string or object of type responseMessage, so we can create a POJO with the ref variable with the same name (java basics :)), so I'm looking for the same solution for dynamic JSON in "Retrofit" . I know that this is a very simple job in regular http clients with an asynchronous task, but this is not the best practice in analyzing REST-Api JSON ! looking at Benchmarks performance, Volley or Retrofit is always the best choice, but I could not cope with the JSON dynamics!

Possible solution that I know

  • Use the old asyc task with parsing the http client. :(

  • Try to convince the RESTapi database developer.

  • Create a custom client for retooling :)

+64
json android gson retrofit
Jun 18 '14 at 7:15
source share
9 answers

Late side, but you can use a converter.

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

Then you use a static class that implements the modified converter:

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

I wrote this sample converter to return a Json response either as a String, Object, JsonObject or Map <String, Object>. Obviously, not all return types will work for every Json, and there is a definite opportunity for improvement. But it demonstrates how to use the converter to convert almost any response to dynamic Json.

+31
Feb 18 '15 at 4:56
source share

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

You must implement the checkResponseMessage method.

+16
Jul 01 '14 at 13:11
source share

Any of your possible solutions will work. What you can also do is send the return type of the Apo Retrofit interface in response. With this answer, you will get an Inputstream body that you can convert to a JSON object and read as you see fit.

See: http://square.imtqy.com/retrofit/#api-declaration - under RESPONSE TYPE TYPE

Update

Retrofit 2 is now missing and with it some changes in the documentation and library.

Look at http://square.imtqy.com/retrofit/#restadapter-configuration there is a request and response body object that you can use.

+10
Jun 18 '14 at 7:59
source share

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

 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 of trying to show you how you can use a different model.

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

+6
Jan 22 '16 at 22:38
source share

Try setting up deserialization with gson-converter as gson-converter 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 (user deserializer)

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

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

Libraries used

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

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

***** Previous answer (the above answer is more recommended) *****

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 the onResponse modification like this

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

you can also check this post for more info on json dynamic parsing

+6
Sep 20 '17 at 11:19 on
source share

I know I'm very late to the party. I had a similar problem and I 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 the type to Object. I chose this approach because only one field in the answer was dynamic (for me, my answer was more complicated), so using a converter would make life difficult. Used Gson to work with the object from there, depending on whether it was a String or Array value.

Hope this helps someone find a simple answer :).

+5
Oct 19 '16 at 14:36
source share

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

  • We can use the GSI adapter type to create a custom adapter for the ResponseMessage type, which dynamically decides how to parse INMONS JSON (using something like if (reader.peek() == JsonToken.STRING) ).

  • Put some meta information describing the type of response in the HTTP header and use it to determine what type information should be fed into the Gson instance.

+3
Jul 02 '14 at 19:05
source share

In addition to what you said -

Use callback Then you can get the fields using the regular get method. For more information, go to javadoc gson.

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

+1
Jul 01 '14 at 13:02
source share

I also ran into this problem. but I'm not sure if this is your case (I am using Retrofit2)

in my case I need to handle errors and success messages.

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 Error POJO,

 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 in json contains data1, data2, then ValidateUserResult.java initialized. if an error occurs, the Error.java class Error.java initialized.

0
Sep 24 '18 at 11:43
source share



All Articles