Making GsonRequest to accept empty list or null array - android

I have a Json data to be pulled from a server. This data contains several objects and arrays.
The first model is as follows:
{
"results": [
{
"id": "17",
"name": "Accessories",
"child": [
{
"id": "371",
"name": "Belt"
},
{
"id": "55",
"name": "Derp"
},
...
]
}
]
}
However, some of the results array doesn't have child array. Instead, it have a String with an empty value.
{
"results": [
{
"id": "19",
"name": "Stuff",
"child": ""
}
]
}
When the code is executed, it returns this line:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING
This is how the Model looks like:
public class CategoryModel {
#SerializedName("id")
private String category_id;
private String name;
private ArrayList<CategoryChildModel> child;
...
}
And this is how I implement the GsonRequest (which using Volley as background asynctask):
private void loadCategory() {
mRequestQueue = Volley.newRequestQueue(getActivity());
String url = Constants.CATEGORIES_LIST;
GsonRequest<CategoryContainer> myReq = new GsonRequest<CategoryContainer>(
Request.Method.GET, url, CategoryContainer.class,
createMyReqSuccessListener(), createMyReqErrorListener());
mRequestQueue.add(myReq);
}
So, anyone knows how to make null object pass through GsonRequest?

Actually your json response should return an empty array not a string for null cases. But if you don't have an option to change server's response then you may try to write a custom json deserializer:
class ChildDeserializer implements JsonDeserializer<ChildHolder> {
#Override
public ChildHolder deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
String currentValueOfChild = json.toString();
Log.d("ChildDeserializer", "ChildDeserializer: child=" + currentValueOfChild);
ChildHolder childHolder = null;
if (json instanceof JsonArray) {
Log.d("ChildDeserializer", "ChildDeserializer: We have an array for 'child'");
Type listType = new TypeToken<List<Child>>() {}.getType();
JsonArray jsonArray= json.getAsJsonArray();
childHolder = new ChildHolder();
childHolder.childList = context.deserialize(jsonArray, listType);
}
return childHolder;
}
}
Your response java model should look like below:
class Response {
List<Result> results;
}
class Result {
private String id, name;
private ChildHolder child;
}
class ChildHolder {
private List<Child> childList;
}
class Child {
private String id, name;
}
Apply deserializer while parsing json to java model:
String jsonTest1 = "{\"results\":[{\"id\":\"17\",\"name\":\"Accessories\",\"child\":[{\"id\":\"371\",\"name\":\"Belt\"},{\"id\":\"55\",\"name\":\"Derp\"}]}]}";
String jsonTest2 = "{\"results\":[{\"id\":\"19\",\"name\":\"Stuff\",\"child\":\"\"}]}";
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(ChildHolder.class, new ChildDeserializer());
Gson gson = gsonBuilder.create();
Response response1 = gson.fromJson(jsonTest1, Response.class);
Response response2 = gson.fromJson(jsonTest2, Response.class);
Also please read this link for further information.

Another option for writing a custom serializer is to simply convert the empty string to an empty array in the JSON. Then your class can remain the way it is:
class ChildDeserializer implements JsonDeserializer<CategoryModel>
{
#Override
public ChildHolder deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context)
throws JsonParseException {
JsonObject obj = json.getAsObject();
JsonElement e = obj.get("child");
if (e.isJsonPrimitive()) // it's a String
{
obj.remove("child");
obj.add("child", new JsonArray());
}
return new Gson().fromJson(obj, CategoryModel.class);
}
}

Related

Avoid adding null POJO objects to parsed array from retrofit response

I need to avoid returning an array with null items after parsing a response from a Web Service, but I cannot figure out how to configure Gson to do so. I intended to do it this way as I don't like having to process the parsed response (having to loop the array again).
Let me show you a basic example of what I mean.
Given this POJO:
public class Item {
#SerializedName("_id")
private String id; // Required field
private String name;
#SerializedName("accept_ads")
private boolean acceptAds;
// Getters and setters
}
And given a JSON response from a webservice:
[
{"_id": "a01",
"name": "Test1",
"accept_ads": true},
{"name": "Test2"}
]
I created this deserializer:
public class ItemDeserializer implements JsonDeserializer<Item> {
#Override
public Item deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
Item item = null;
JsonObject jsonObject = json.getAsJsonObject();
if (jsonObject.has("_id")) {
item = new Item();
item.setId(jsonObject.get("_id").getAsString());
if (jsonObject.has("name")) {
item.setName(jsonObject.get("name").getAsString());
}
if (jsonObject.has("accept_ads")) {
item.setAcceptAds(jsonObject.get("accept_ads").getAsBoolean());
}
}
return item;
}
}
Then, I create my Gson and Retrofit instances like this:
Gson gson = new GsonBuilder().registerTypeAdapter(Item.class, new ItemDeserializer()).create();
mRetrofit = new Retrofit.Builder().addConverterFactory(GsonConverterFactory.create(gson)).build();
Unfortunately, the returned ArrayList<Item> contains both an Item and a null value.
UPATE
Thanks to #deluxe1 in the comments below, I read the linked SO thread, which by itself isn't what I was looking for, as it's about serializers instead of deseralizers. However, another answer in that thread specifically focuses on deserializers.
Unfortunatedly, I'm unable to make it work. Please notice that, in that case, is about null JsonElements inside the JSON HTTP body response, which isn't exactly what I'm trying to achieve. In my case, a JsonElement might not be null, but lacking required fields which, in the end, makes it invalid (therefore, null).
Given the example classes above, I created the following deserializer:
public class NonNullListDeserializer<T> implements JsonDeserializer<List<T>> {
#Override
public List<T> deserialize(JsonElement json, Type typeOfT, JsonDeserializationcontext context) throws JsonParseException {
Gson gson = new GsonBuilder().create();
List<T> items = new ArrayList<>();
for (final JsonElement jsonElement : json.getAsJsonArray() {
T item = gson.fromJson(jsonElement, typeOfT);
if (item != null) {
items.add(item);
}
}
return items;
}
}
Of course, I remembered to register it as a TypeAdapter in Gson.
The thing is, typeOfT is java.util.ArrayList<com.example.Item> when that method gets invoked (But why??). As a result, the line T item = gson.fromJson(jsonElement, typeOfT) doesn't get called with the inner class (Item, in this case), causing this exception:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at path $
AFAIK, type erasure makes it impossible to do the following:
T item = gson.fromJson(jsonElement, T.class)
NonNullListDeserializer requires small modifications:
class NonNullListDeserializer<T> implements JsonDeserializer<List<T>> {
#Override
public List<T> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (json instanceof JsonArray) {
final JsonArray array = (JsonArray) json;
final int size = array.size();
if (size == 0) {
return Collections.emptyList();
}
final List<T> list = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
// get element type
Type elementType = $Gson$Types.getCollectionElementType(typeOfT, List.class);
T value = context.deserialize(array.get(i), elementType);
if (value != null) {
list.add(value);
}
}
return list;
}
return Collections.emptyList();
}
}
Now, you need to deserialise your JSON payload as below:
Gson gson = new GsonBuilder()
.registerTypeAdapter(Item.class, new ItemDeserializer())
.registerTypeAdapter(List.class, new NonNullListDeserializer<>())
.setPrettyPrinting()
.create();
Type itemListType = new TypeToken<List<Item>>() {}.getType();
List<Item> response = gson.fromJson(json, itemListType);

Gson: Mapping multiple json objects to a single object

Suppose we have this simple json:
"Book":
{
"title": "Hello world",
"metadata":
{
"author": "Jeff"
}
}
Is there an easy way to ignore the metadata object and put author directly into a books object using gson? Should i use a deserializer for that or is there an easier way?
I need a class like this:
public class Book
{
private String title;
private String author;
//...
}
Use Deserializer. Something like this :
public class BookDeserializer implements JsonDeserializer<Book> {
#Override
public Book deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context)
throws JsonParseException {
final JsonObject jsonObject = json.getAsJsonObject();
final String title = jsonObject.get("title").getAsString();
JsonElement metaElement = jsonObject.get("metadata");
final String author = metaElement.get("author").getAsString();
final Book book = new Book();
book.setTitle(title);
book.setAuthor(author);
return book;
}
}
How to use it:
sGson = new GsonBuilder()
.registerTypeAdapter(Book.class, new BookDeserializer())
.create();
Note : I prefer to use Constant instead of hardcoded strings but it's just for the example here ;)

How to parse JSON response Array of object, where array is without key name

I am trying to parse json result without array name. Here is my json response:
[
{
"Id": 2293,
"Name": "Dr.",
"Active": true
},
{
"Id": 2305,
"Name": "Mr.",
"Active": true
},
{
"Id": 2315,
"Name": "Mrs.",
"Active": true
}
]
How to parse this using com.squareup.retrofit2:retrofit:2.1.0 library?
Create One Class Like,
class Test {
public List<TestValue> testValues;
}
Then call API,
Call<List<Test>> getTestData(#Field("xyz") String field1);
Call <List<Test>> call = service.getTestData("val");
call.enqueue(new Callback<List<Test>>() {
#Override
public void onResponse(Call<List<Test>> call, Response<List<Test>>
response) {
List<Test> rs = response.body();
}
#Override
public void onFailure(Call<List<Test>> call, Throwable t) {
}
});
User Your Model class, this is only for example purpose.
Normally you can parse as
String response = "[{"Id": 2293,"Name": "Dr.","Active": true},{"Id": 2305,"Name": "Mr.","Active": true},{"Id": 2315,"Name": "Mrs.","Active": true}]";
try {
JSONArray ja = new JSONArray(response);
for (int i = 0; i < ja.length(); i++) {
JSONObject jo = ja.getJSONObject(i);
String id = jo.getString("Id");
String name = jo.getString("Name");
String active = jo.getString("Active");
}
} catch (JSONException e) {
e.printStackTrace();
}
If you want to parse it using Model Class then your Model Class will be for Retrofit
class Response
{
#SerializedName("Id")
#Expose
private String id;
#SerializedName("Name")
#Expose
private String name;
#SerializedName("Active")
#Expose
private String active;
}
and define Callback for retrofit like that
Call<List<Meeting>> getMeetings(#Field String data );
Hope this will help

Dynamically tags parsing Json Data using gson

I have a JSoN data like this:
{
"data": {
"noofCity": "1",
"City 1": [
{
"id": "12",
"title": "Delhi"
}
]
},
"success": true
}
Now based on noofCity next tag City 1 will be generated. If noofCity will be 2 then there are two tag City 1 and City 2. Then how can I parse it using Json? Please tell me how can I generate my POJO class structure.
Your POJOs should look like below:
Main POJO for Response:
public class Response {
Data data;
boolean success;
}
For Data
public class Data {
int noofCity;
Map<String, List<City>> cityMap;
void put(String key, List<City> city){
if(cityMap == null){
cityMap = new HashMap<>();
}
cityMap.put(key, city);
}
public void setNoofCity(int noofCity) {
this.noofCity = noofCity;
}
public int getNoofCity() {
return noofCity;
}
}
For City
public class City {
int id;
String title;
}
But one of the most important think is a way how to deserialise Data. You have to prepare your own deserialiser for this, and define way how to fill HashMap as is shown in the code below:
public class DataDeserializer implements JsonDeserializer<Data> {
#Override
public Data deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
Data result = new Data();
Gson gson = new Gson();
JsonObject jsonObject= json.getAsJsonObject();
result.setNoofCity(jsonObject.get("noofCity").getAsInt());
for(int i =1; i<=result.getNoofCity() ; i++ ){
List<City> cities= gson.fromJson(jsonObject.getAsJsonArray("City "+ i), List.class);
result.put("City "+ i, cities);
}
return result;
}
}
And now you can deserialise you json
Gson gson = new GsonBuilder()
.registerTypeAdapter(Data.class, new DataDeserializer())
.create();
Response test = gson.fromJson(json, Response.class);

Deserialize JSON object to String Android

I am trying to implement deserialization to parse json object as a string, but my custom deserializable class is not being called.
JSON which needs to be parsed
{
"status": true,
"student": [
{
"id": 1,
"name": "",
"age": "",
"title": "",
}
]
}
My Deserializable class
public class MyDeserializer implements JsonDeserializer<StudentData> {
#Override
public StudentData deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) {
try {
String content = je.getAsJsonObject().get("student").getAsString();
return new Gson().fromJson(content, StudentData)
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
Register my deserializer:-
MyDeserializer myDeserializer = new MyDeserializer();
Gson gson = new GsonBuilder().registerTypeAdapter(NotificationResponse.class, myDeserializer).create();
mRestAdapter = new RestAdapter.Builder().setServer(baseUrl).setConverter(new GsonConverter(gson)).setLogLevel(RestAdapter.LogLevel.FULL).setRequestInterceptor(new RequestInterceptor()
{
#Override
public void intercept(RequestFacade requestFacade) {
}
}).build();
I think this tutorial will help you implement a Deserializer(and might introduce some new concepts)
Try it, and see if it work for you!
For something that simple I don't think adding Gson as a dependency is worth it.
Example:
JSONObject jObj = new JSONObject(theJsonYouPostedAbove);
boolean status = jObj.getBoolean("status");
JSONArray jArr = jObj.getJSONArray("student");
for (int i = 0; i < jArr.length(); i++) {
JSONObject jo = jArr.getJSONObject(i);
int id = jo.getInt("id");
String name = jo.getString("name");
...
}

Categories

Resources