2

Here is my sample JSON when a user authenticates successfully

{
    "status": "success",
    "message": "",
    "data": {
        "_id": {
            "$id": "..."
        },
        "email": "...",
        "name": "...",
        "mobile": "...",
        "mobile_status": "...",
        "email_status": "..."
    }
}

And when user credentials are wrong

{
    "status": "error",
    "message": {
        "msg": [
            "Login detail incorrect"
        ]
    },
    "data": ""
}

Notice the change in datatypes of message & data. Using retrofit fit with this I received famously error Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $

To resolve this I followed this example to resolve the issue. Which did resolve the OBJECT/ARRAY issue.

I have 3 Models LoginResponse,MessageResponse,DataResponse and this is how I am trying to get access to MessageResponse & DataResponse

I'm not sure if i can even use 2

Gson messageDeserializer = new GsonBuilder().setLenient().registerTypeAdapter(LoginResponse.class, new MessageDeserializer()).create();
Gson dataDeserializer = new GsonBuilder().setLenient().registerTypeAdapter(LoginResponse.class, new DataDeserializer()).create();
builder.addConverterFactory(GsonConverterFactory.create(messageDeserializer));
builder.addConverterFactory(GsonConverterFactory.create(dataDeserializer));

I don't think these custom JsonDeserializer are working the way its supposed to. I'm unable to cast returned response from these JsonDeserializer. The only way I can access them as string only which isn't even a proper JSON formatted string.

Also switching back order of .addConverterFactory sometimes causes a crash when i access LoginResponse model

Skyyy
  • 1,539
  • 2
  • 23
  • 60

1 Answers1

2

Do you have access to the network error & response codes?. That way, you may create a switch statement that explicitly defines how you would deserialize ANY response

For example,

Gson switchToDeserialise(int errorCode){

///i assume 200 means correct response. And 404 means there's an errorCode

Gson deserializer = new GsonBuilder().create();

switch(errorCode){
case 200:
return new GsonBuilder().setLenient().registerTypeAdapter(LoginResponse.class, new DataDeserializer()).create();
case 404:
return new GsonBuilder().setLenient().registerTypeAdapter(LoginResponse.class, new MessageDeserializer()).create();
default:
return deserializer;}}

Note: The above code has not been tested.

=================================== BIT OF A CRASH EDIT REALLY =============================================================================

Based on the conversation we had, and my observation on both the success & error response, i have this un-tested code/suggestion :

    //in the retrofit response callback, get the status via JSONObject

JSONObject status = new JSONObject(response.toString()); throws Exception
String statusString = status.optString("status");

//now take the result of the string to a switch statement

Gson switchToDeserialise(String statusString){
///from what you've shown in the success & failure responses, status is either success or error, so...

Gson deserializer = new GsonBuilder().create();

switch(statusString){

case "success":
return new GsonBuilder().setLenient().registerTypeAdapter(LoginResponse.class, new DataDeserializer()).create();
case "error":
return new GsonBuilder().setLenient().registerTypeAdapter(LoginResponse.class, new MessageDeserializer()).create();
default:
return deserializer;

}

}
Jendorski Labs
  • 447
  • 2
  • 9
  • 20
  • The response code is always(on login success/fail) 200. The only way I can detect login success/fail is by looking at success in the JSON. I'm able to detect using `status` if user login was a success/failed but When user login is failed I am not able to access fail message using its model – Skyyy Feb 07 '19 at 14:08
  • Ok, i understand. Here's an idea: Upon receiving the response, use the `JSONObject` library to request the result of `status`, then formulate a switch statement, to define your deserialization. – Jendorski Labs Feb 07 '19 at 14:41
  • My bad I just realized that I do have different response code 200 for success login and 207 for login failed. But how do I pass in Custom CoverterFactory inside `onResponse` method or is there a way to detect response code before that? For now, on basis of status success/error state, I'm manually deserializing response using `new Gson().fromJson(data, DataResponse.class);` – Skyyy Feb 08 '19 at 10:09
  • Also when the login failed (POJO I generated online) I get nullPointerException on accessing getMsg method (after passing JSON in the Deserializer) – Skyyy Feb 08 '19 at 10:11
  • If its `retrofit` you use, you can get the errorCode from the Response#code and from both the `onResponse` and `onError` methods so you can put the `switchToDeserialise` method in both call backs. In relation to the NPE you get from the getMsg should be, IMO, `String errorMessage = getMessage().getMsg(0)`. – Jendorski Labs Feb 09 '19 at 18:16