gson handle: Expected BEGIN_OBJECT but was BEGIN_ARRAY - android

I am using Retrofit and Gson to make API calls. I have a problem with responses from server. For some attributes it is sending empty JSONArray instead of null JSONObject. e.g.:
in normal situation:
{
"pagination": {
"links": {
"next": "http://api.com/nextlink"
}
}
}
but sometimes when the "links" is empty, the server sends me this:
{
"pagination": {
"links": []
}
}
which cause java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY.
I know that I can handle it with using custom JsonDeserializer for object "Pagination" and registerTypeAdapter when creating GsonBuilder.
But my question is: It is possible to handle this cases in general for all responses? I don't have access to API so I cannot change it and I don't know for which attributes I can get empty JSONArray instead of JSONObject that is expected.
Thank you.

You can use JSONTokener to get normal Object after check it with instanceof function, try bellow:
String jsonData = "{...}"; //your json data string
JSONTokener tokener = new JSONTokener(jsonData);
try {
Object parsedObj = tokener.nextValue();
if (parsedObj instanceof JSONObject) {
//do something here
}else if (parsedObj instanceof JSONArray){
//do something here
}
}catch(Exception ex){}
Read more here enter link description here

Try this to check if it is an object or not
yourJson.get("links").isJsonObject()
check these methods
isJsonArray()
isJsonObject()
isJsonNull()

Related

Check which type of data is coming as response using Retrofit

As I'm using Retrofit, I've designed all the POJOs and it was working flawlessly. API is designed in such a way that it will send the required data if the data is of current date or of future dates but not for past dates. In the response, I'll get a JSON response contains a combination of JSON objects and an array as a value of a JSON object and POJOs are according to that. Now if there is no record for present and future dates then I'll receive a string instead of an array and that leads to API error java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING. So what I wanna know if there is any way that I can determine - what I'm receiving an array or a string? and how to update POJO according to that to avoid that error.
JSON response when server has no data
{
"Result_Code": "ND",
"Result_Status": "Success",
"Result_Message": "No record found in database.",
"Result_Output": "",
"updatedDate": "20-07-2017 10:44:37"
}
JOSN response will be same when server has data but with one difference
{
"Result_Code": "ND",
"Result_Status": "Success",
"Result_Message": "record found in database.",
"Result_Output": [{multiple elements},
{multiple elements},
{multiple elements}....],
"updatedDate": "20-07-2017 10:44:37"
}
Pojo class named ResponseModel
public class ResponseModel {
private String Result_Code;
private String Result_Status;
private String Result_Message;
private Object Result_Output;
private String updatedDate;
...
}
using Object you can morph as below
call.enqueue(new Callback<ResponseModel>()
{
#Override
public void onResponse(Response<ResponseModel> response, Retrofit retrofit)
{
parseData(); // get other data from ResponseModel Class
if (response.getResultOutput() instanceof List<POJO>)
{
doSomething();
}
else if (response.getResultOutput() instanceof String)
{
doSomething();
}
else //must be error object
{
doSomething();
}
}
#Override
public void onFailure(Throwable t)
{
///Handle failure
}
});
using instanceof you check desired Object type
Where List<POJO> used for multiple elements Model
*check updated solution for parsing ArrayList from response object
hopefully it might be work as you want
catch your json in debug mode and generate pojo class with link below. then compare your class and see difference
http://www.jsonschema2pojo.org/
you can try this method.
try {
callArrayPojo();
} catch (IllegalStateException e) {
callStringPojo();
} catch (Exception e) {
//other}
or you can get ResultMessage generic type
...
private String Result_Code;
private String Result_Status;
private T Result_Message;
...

I having the two json response from the server side with the same "key".The json is posted below

{"data":
{
"userId":"+919923911289",
"inTime":"2016-07-25 12:09:47+05:30",
"outTime":"0",
"totalTime":"0",
"type":"attendance"
}
}
And the second json response is
{"data":"please try again..."}
I am using retrofit to get the response . For the first json i have created the Gson model.But , when the response is in the second json format i didn't have any response model for it.
I am displaying the data using the GsonModel for the first json response.
And if there is second type of response what should i do here.Will i use the TypeToken here or something else.
And it also giving me the parsing exception wich i understand.But , i didn't know what i have to do.
Create a another model
class ErrorModel{
String data;
}
and in your main method:
try{
GsonModel model = gson.fromJson(response,GsonModel.class);
// ...
}catch(JsonSyntaxException e){
ErrorModel model = gson.fromJson(response,ErrorModel.class);
// show error
}
if (data.equalsIgnoreCase("please try again...") {
//show error
} else {
//do your work with response
}
if(data.has("userId") && !data.isNull("userId") ){
String user_Id=data.getString("userId");
}else{
////show message "please try again..." here....
}

How to get the retrofit response in string format ? And convert that response into the model? [duplicate]

{"data":
{
"userId":"+919923911289",
"inTime":"2016-07-25 12:09:47+05:30",
"outTime":"0",
"totalTime":"0",
"type":"attendance"
}
}
And the second json response is
{"data":"please try again..."}
I am using retrofit to get the response . For the first json i have created the Gson model.But , when the response is in the second json format i didn't have any response model for it.
I am displaying the data using the GsonModel for the first json response.
And if there is second type of response what should i do here.Will i use the TypeToken here or something else.
And it also giving me the parsing exception wich i understand.But , i didn't know what i have to do.
Create a another model
class ErrorModel{
String data;
}
and in your main method:
try{
GsonModel model = gson.fromJson(response,GsonModel.class);
// ...
}catch(JsonSyntaxException e){
ErrorModel model = gson.fromJson(response,ErrorModel.class);
// show error
}
if (data.equalsIgnoreCase("please try again...") {
//show error
} else {
//do your work with response
}
if(data.has("userId") && !data.isNull("userId") ){
String user_Id=data.getString("userId");
}else{
////show message "please try again..." here....
}

A value of a json str sometimes is a String, sometimes is a object, how could i use gson to parse it

Like the title , my json str sometimes like this:
{
"data": {
"changebaby": "no change",
"changemama": {
"mamacontext": "mama is a good mama",
"mamaico": "",
"mamatitle": "mama"
}
}
}
sometimes it like this:
{
"data": {
"changebaby": "no change",
"changemama": "no change"
}
}
as you see,the value of the "changebaby" key and the "changemama" key sometimes is a String, sometimes is a object, how should i parse it by gson? Could anybody help me?
Don't use the android api to parse the json string, need to use the gson lib of google to parse the json string, could anybody help me?
if(jsonObject.optJSONObject("changemama") != null)
{
JSONObject changemama=jsonObject.optJSONObject("changemama");
//Its JSON object, do appropriate operation
}
else if(jsonObject.optString("changemama") != null)
{
//Its string, do appropriate operation
}
if you have more number of possibilities like boolean or int or long, refer this
optJSONObject
Returns the value mapped by name if it exists and is a JSONObject, or
null otherwise.
Or go with the way lawrance has given : Determine whether JSON is a JSONObject or JSONArray
Try with this :
JSONObject changemama=jsonObject.optJSONObject("changemama");
if(changemama== null){
String str=jsonObject.optString("changemama");
}
Try this code.
JSONObject data;
try {
data = jsonObj.getJSONObject("changemama");
// do stuff
} catch (JSONException e) {
data = jsonObj.getString("changemama");
// do stuff
}
try this :
if(obj.has("changemama")){
if(obj.optString("changemama").length() > 0){}
else if(obj.optJSONObject("changemama").length() > 0){}}
To simplyfy android development, we can ask for the backend developers to change the Mobile API.The new API could returen the json string that cann't change the value.The value of all keys cann't sometimes be a string, sometimes a object.

Parsing an inconsistent json(different json structure for same API call) using Gson Library

I am working on a Android app, where I have a web service call and that service returns me a Json Object as response but in some cases it is returning a different structure, please find the structure:
Case 1: Json Structure
The Json have keys called "from" and "to" which are of type String.
Ex: "to": "BVRT",
"from": "NS",
Case 2: Json Structure
The Json have keys called "from" and "to" which are of custom object.
ex:
"from": {
"name": "BANGALORE CY JN",
"code": "SBC"
},
"to": {
"name": "BHIMAVARAMTOWN",
"code": "BVRT"
},
As there is lot of nested objects I am using Gson Library to parse the json object which makes life easier instead of manual parsing.
Issue Facing
How do I construct my pojo class since there are two different structures for same api call,I have tried JsonIgnore but it didn't worked as it has the same json key in both the cases.
Please find the screenshot for better understanding which have the complete Json structure, hoping a reply with sample code snippet as this has been a blocker for which we could not proceed to further functionality of the app.Json Structure
After research of few hours I got a solution for this, here my solution works without creating separate model classes to handle different Json structures for same API call:
Model Class
public class TrainDetails {
#SerializedName("to")
private String toString;
#SerializedName("from")
private String fromString;
#SerializedName("to")
private ToStationPnrPojo fromStationObject;
#SerializedName("from")
private ToStationPnrPojo toStationObject;
}
Now construct a Gson object using ExclusionStrategy, here you can specify which fields you need to exclude in our model class in order to match the Json structure sent from server side.
try {
JSONObject jsonObject=new JSONObject(result);
JSONArray jsonArray=jsonObject.optJSONArray("train");
if(jsonArray.length()>0){
JSONObject jsonObject1= jsonArray.getJSONObject(0);
Object test=jsonObject1.get("to");
if(test instanceof String){
Log.e("Test","Instance OF String");
CustomExclusionStrategy ges = new CustomExclusionStrategy(TrainDetails.class, "toStationObject","fromStationObject");
gson= new GsonBuilder().setExclusionStrategies(new CustomExclusionStrategy[]{ges}).create();
}else{
Log.e("Test","Instance OF Custom object");
CustomExclusionStrategy ges = new CustomExclusionStrategy(TrainDetails.class, "toString","fromString");
gson= new GsonBuilder().setExclusionStrategies(new CustomExclusionStrategy[]{ges}).create();
}
}
} catch (JSONException e) {
e.printStackTrace();
}
We can pass the fields names to CustomeExclusionStrategy specifying Gson that exclude these fields while parsing the Json from server.
CustomExclusionStrategy
public class CustomExclusionStrategy implements ExclusionStrategy {
private final List<String> _skipFields = new ArrayList<String>();
private final Class<?> _clazz;
public CustomExclusionStrategy(Class<?> clazz, String... fields) {
_clazz = clazz;
for (String field : fields) {
_skipFields.add(field);
}
}
#Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
#Override
public boolean shouldSkipField(FieldAttributes f) {
return f.getDeclaringClass() == _clazz
&& _skipFields.contains(f.getName());
}
}
Hope this will be helpfull if any other were facing the similar issue while dealing with parsing of Json which is not in the client application control.

Categories

Resources