dynamically define collection or object type variable in java

1.2k Views Asked by At

I have seen number of matching questions but non answered my problem. I want to make following code snippet dynamic in a way that I am not sure that my API will return a single object or an array of objects.

List<Article> article = null;

I have seen different examples like below, but this does not answer my question:

List<Animal> animals = new ArrayList<>();

The thing is, I have article variable defined in my Java gson model class, and I really have no idea how can I handle a response and make my article to behave dynamically. So, if there is a single object then it should act like single object instead of List, and if there is array of objects then it should act like List.

I am also not sure if something like this will work or not but I am looking for following type of functionality in any do able form:

<T> Article article = null;

Note: I have also seen this Generic Type Doc. but this doesn't help to solve my problem.

2

There are 2 best solutions below

2
Jeff Stewart On

Consider extending the ArrayList with methods that internally perform aggregation methods so that externally it behaves the same whether there is 0, 1, or N number of internal objects.

class Articles extends ArrayList<Article> {...}
0
Yavuz Tas On

Although @Jeff Stewart's answer might be a viable solution I would like to add something. If the type-safety is not an issue for your needs, you can consider also defining your article variable as Object. However, you should check your variable later when you use, something like:

if(article instanceof Collection){
  // article is an array of objects  
} else {
  // article is a single object
}

UPDATE: Thanks to @Jeff Stewart's idea I have implemented a more appropriate solution that can handle both single and multiple values in a single type.

Considering your API response something like this:

"article": {"name":"article1",...} 
 - or -  
"article": [{"name":"article1",...}, {"name":"article2",...}]

Let's create ArticleList derived from java.util.ArrayList

/**
 * Custom type to handle both single Article and ArrayList<Article> types
 *
 * @author Yavuz Tas
 *
 */
public class ArticleList extends ArrayList<Article> {

}

After we implement a custom JsonAdapter for ArticleList type

/**
 * Custom JsonAdapter for GSON to handle {@link ArticleList} converstion
 * 
 * @author Yavuz Tas
 *
 */
public class ArticleListJsonAdapter extends TypeAdapter<ArticleList> {

    /*
     * We just create another instance of GSON here in order to reuse their
     * predefined object and collection adapters
     */
    private static final Gson gson = new Gson();
    private static final TypeAdapter<Article> objectTypeAdapter = gson.getAdapter(Article.class);
    private static final TypeAdapter<List<Article>> listTypeAdapter = gson.getAdapter(new TypeToken<List<Article>>() {
    });

    @Override
    public void write(JsonWriter out, ArticleList list) throws IOException {

        /*
         * Since we do not serialize ArticleList by gson we can omit this part but
         * anyway we can simply implement by reusing listTypeAdapter
         */
        listTypeAdapter.write(out, new ArrayList<Article>(list));

    }

    @Override
    public ArticleList read(JsonReader in) throws IOException {

        ArticleList deserializedObject = new ArticleList();

        // type of next token
        JsonToken peek = in.peek();

        // if the json field is single object just add this object to list as an
        // element
        if (JsonToken.BEGIN_OBJECT.equals(peek)) {
            Article object = objectTypeAdapter.read(in);
            deserializedObject.add(object);
        }

        // if the json field is array then implement normal array deserialization
        if (JsonToken.BEGIN_ARRAY.equals(peek)) {
            List<Article> list = listTypeAdapter.read(in);
            deserializedObject.addAll(list);
        }

        return deserializedObject;
    }
}

And last we register our adapter to our article field in GSON model that is used for API response and change its type to ArticleList:

@JsonAdapter(value = ArticleListJsonAdapter.class)
@SerializedName("article")
@Expose
private ArticleList article;

Please note that any single Article response automatically added to ArticleList as a list element. You can alter this behavior by changing the implementation in the read method of ArticleListJsonAdapter.

I hope this helps too. Cheers!