0

My response from the server is a list of different types of JSON objects, which depends on each responses' "type". I have different beans for each response type. Can Retrofit use appropriate Bean class for each response (JSON Object)in the list(JSON Array)?

   {
    "cards": [2]
        0:  {
            "type": "A"
            "ID": 24
            "author_name": "ari"
            "title": "BB"
            "permalink": ""
            "created_date": "2015-12-18 17:17:00"
            "post_date": "Dec 18 2015"
            "thumbnail": ""
            "summary": "Stars"
            "thumbSmall": "100.jpg"
            "androidXL": "500.jpg"
        }-
        1:  {
            "type": "B"
            "_id": "3290"
            "read_count": 0
            "categories": [1]
                0:  "abc"      
            "title": "New"
            "isSticky": false
            "section": [0]
            "author": "zzz"
            "india": false
            "update_time": 1450415789694
            "summary": "Social"
            "scoreId": "nz"
            "isActive": true
            "image": ""
            "timestamp": 1450385165210
            "events": [1]
                0:  "nz"
            "teams": [0]
            "slug": "new"
            "isDeleted": false
            "score_str": "SL"
        }
    }

As a workaround, I have created a new Bean class, with all possible fields, almost half the fields are null for each JSON Object.
Is there a better way of doing this?

1 Answer 1

2

You can create a super class which contains data common for all types and extend it:

public abstract class SuperItem {

 public enum Type {
    @SerializedName("A")
    TYPEA(1),

    @SerializedName("B")
    TYPEB(2);

    Type(final int value) {
        this.value = value;
    }

public static Type fromInteger(int i) {
        switch (i) {
            case 1:
                return TYPEA;
            case 2:
                return TYPEB;
            default:
                Log.d("SuperItem", "No existing type for integer: " + i);
                return null;
        }
    }
}

}

@SerializedName("type") private Type type;
}

public class AItem extends SuperItem {

}
public class BItem extends SuperItem {

}

Retrofit by default relies on GSON for JSON deserialization. You will need to create custom Gson deserializer for your implementation and set it to your RestAdapter:

GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(SuperItem.class, new SuperItemDeserializer());
Gson gson = builder.build();
 Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create(gson))
            .build();

Your custom deserializer can look like this:

public class SuperItemDeserializer implements     JsonDeserializer<SuperItem> {

@Override
public SuperItem deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
    Gson gson = new Gson();
    SuperItem item = null;
    JsonObject rootObject = json.getAsJsonObject();
    int typeInt = rootObject.get("type").getAsInt();
    SuperItem.Type type = SuperItem.Type.fromInteger(typeInt);
    switch (type) {
        case TYPEA:
            item = gson.fromJson(json, AItem.class);
            break;
        case TYPEB:
            item = gson.fromJson(json, BItem.class);
            break;
        default:
            Log.d("TAG", "Invalid type: " + typeInt);
    }
    return item;
}
}

Hope the naming in the example is not too bad and you'll get the idea :)

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.