Fran Marzoa
Fran Marzoa

Reputation: 4544

ClassCastException with Gson and class inheritance from generic ArrayList

I'm having trouble parsing a custom class instance to/from Json with Gson. This is a simplified version of the code I'm dealing with:

import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;

import java.util.ArrayList;
import java.util.Arrays;

class IntegerArrayList extends ArrayList<Integer> {
    public static IntegerArrayList fromJson(String json) {
        TypeToken<IntegerArrayList> integerArrayListTypeToken = new TypeToken<IntegerArrayList>() {} ;
        IntegerArrayList integerArrayList = new GsonBuilder()
                .enableComplexMapKeySerialization()
                .create()
                .fromJson(json, integerArrayListTypeToken.getType());
        return integerArrayList;
    }

    public String toJson() {
        TypeToken<IntegerArrayList> integerArrayListTypeToken = new TypeToken<IntegerArrayList>() {};
        return new GsonBuilder()
                .enableComplexMapKeySerialization()
                .setPrettyPrinting()
                .create()
                .toJson(this, integerArrayListTypeToken.getType());
    }

    public IntegerArrayList(Integer... items) {
        super();
        addAll(Arrays.asList(items));
    }
}

public class GsonTest {
    public static void main(String[] args) {
        IntegerArrayList integerArrayList = new IntegerArrayList(1, 2, 3, 4, 5);
        String json = integerArrayList.toJson();
        IntegerArrayList integerArrayList1 = IntegerArrayList.fromJson(json);
    }
}

I get this error:

Exception in thread "main" java.lang.ClassCastException: java.util.ArrayList cannot be cast to IntegerArrayList
    at IntegerArrayList.fromJson(GsonTest.java:13)
    at GsonTest.main(GsonTest.java:36)

So the problem here is that instead of returning an IntegerArrayList instance, it's returning the ArrayList from which it inherits from.

Is there any chance to get an IntegerArrayList directly from Gson? How?

Thanks a lot in advance!

Upvotes: 0

Views: 423

Answers (2)

Marcono1234
Marcono1234

Reputation: 6914

The ClassCastException is caused by a bug in Gson which causes it to create standard JDK collection instances, even if they are incompatible with the deserialized type.

However, the underlying issue here (which would remain once that Gson bug is fixed), is that your IntegerArrayList class does not have a no-args constructor. Therefore Gson cannot create an instance of your class. You can either solve this by adding a no-args constructor (IntegerArrayList() { ... }, visibility does not matter), or alternatively by registering an InstanceCreator.

Upvotes: 1

Fran Marzoa
Fran Marzoa

Reputation: 4544

OK, I have just written a custom adapter and it works fine. It's going to be a bit more complicated with the real code, though, but at least I know who to solve this.

Here's the code:

import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;

class IntegerArrayList extends ArrayList<Integer> {
    String customField = "test";

    public IntegerArrayList(Integer... items) {
        super();
        addAll(Arrays.asList(items));
    }

    public static IntegerArrayList fromJson(String json) {
        IntegerArrayList integerArrayList = new GsonBuilder()
                .registerTypeAdapter(IntegerArrayList.class, new JsonAdapter())
                .enableComplexMapKeySerialization()
                .create()
                .fromJson(json, IntegerArrayList.class);
        return integerArrayList;
    }

    public String toJson() {
        return new GsonBuilder()
                .registerTypeAdapter(IntegerArrayList.class, new JsonAdapter())
                .enableComplexMapKeySerialization()
                .setPrettyPrinting()
                .create()
                .toJson(this, IntegerArrayList.class);
    }

    public static class JsonAdapter extends TypeAdapter<IntegerArrayList> {
        @Override
        public void write(JsonWriter out, IntegerArrayList value) throws IOException {
            out.beginObject();
            out.name("customField").value(value.customField);
            out.name("items");
            out.beginArray();
            for (Integer v : value) {
                out.value(v);
            }
            out.endArray();
            out.endObject();
        }

        @Override
        public IntegerArrayList read(JsonReader in) throws IOException {
            IntegerArrayList rtn = new IntegerArrayList();
            in.beginObject();
            while (in.hasNext()) {
                switch (in.nextName()) {
                    case "customField":
                        rtn.customField = in.nextString();
                        break;
                    case "items":
                        in.beginArray();
                        while (in.hasNext()) {
                            rtn.add(in.nextInt());
                        }
                        in.endArray();
                }
            }
            in.endObject();
            return rtn;
        }
    }
}

public class GsonTest {
    public static void main(String[] args) {
        IntegerArrayList integerArrayList = new IntegerArrayList(1, 2, 3, 4, 5);
        String json = integerArrayList.toJson();
        IntegerArrayList integerArrayList1 = IntegerArrayList.fromJson(json);
    }
}

Upvotes: 0

Related Questions