0

Using Gson to deserialize objects of a parameterized class Container<T>, results in the java.lang.ClassCastException for certain values of T, e.g. for a simple Record type consisting of String and List<String> fields:

com.google.gson.internal.LinkedTreeMap cannot be cast to Record

Interestingly enough, the same code works when calling the fromJson() method inline, i.e. the following statement does return a valid value:

Container<Record> value = new Gson().fromJson(
  json, new TypeToken<Container<Record>>(){}.getType());

The definition of Container<T> is simple, too:

public class Container<T> {
  private static final Gson PARSER = new Gson();
  private String id;
  private List<T> content;
  private Object data;
  public static <T> Container<T> deserialize(String json, Class<T> type) {
    return PARSER.fromJson(json, new TypeToken<Container<T>>(){}.getType());
  }
}

Changing the deserialize() method to non-static does not resolve the issue.

Any ideas?

PNS
  • 19,295
  • 32
  • 96
  • 143
  • I've tested this code with this `JSON`: `{"id":"32874394djs","content":[{"name":"ABC","labels":["a","b","c"]}],"data":"Extra data"}` where record contains `name` and `labels` list. I serialised it to `JSON` and deserialised properly back using `new TypeToken>() {}.getType()` type. Could you post your `JSON` and `Record` class? Right now it is not possible to recreate this bug. From other side storing `PARSER` as static field in `POJO` class does not sound as good idea. They should be separated. – Michał Ziober Mar 15 '19 at 07:50
  • Please read "How to create a [mcve]". Then use the [edit] link to improve your question (do not add more information via comments). Otherwise we are not able to answer your question and help you. – GhostCat Mar 15 '19 at 08:20

1 Answers1

2

You want ParameterizedType my solution is create

    public static <T> Container<T> deserialize(String json, Class<T> clazz) {
        Type type = TypeToken.getParameterized(Container.class,clazz).getType();
        return new Gson().fromJson(json, type);
    }

problem is T because Java does not know what i kind and generate Type of T

    public static <T> Container<T> sec(String json, Class<T> clazz) {
        Type type1 = new TypeToken<Container<T>>() { }.getType();
        Type type = TypeToken.getParameterized(Container.class,clazz).getType();
        System.out.println(type1);   //==>pl.jac.container.Container<T>
        System.out.println(type);    //==>pl.jac.container.Container<pl.jac.container.Record>
        return new Gson().fromJson(json, type);
    }

this is test for more example to correct run test testContainerRecord2 is for your problem

import java.lang.reflect.Type;
import org.junit.Test;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import static org.junit.Assert.assertEquals;

public class ContainerTest {

    @Test
    public void testContainerRecord() {
        //given
        String json = "{\"id\":\"new ID\",\"content\":[{\"id\":\"50e0300a-6668-42b3-a474-81a6a08f773f\"},{\"id\":\"f0bee3f3-2c40-4b44-8608-a6fedb226b7a\"}],\"data\":\"AAAAAAAA\"}";
        //when

        Container<Record> containerRecord = Container.deserializeClass(json, ContainerRecord.class);
        //then
        assertEquals("50e0300a-6668-42b3-a474-81a6a08f773f", containerRecord.content.get(0).id);
        assertEquals("f0bee3f3-2c40-4b44-8608-a6fedb226b7a", containerRecord.content.get(1).id);
    }

    @Test
    public void testContainerRecord2() {
        //given
        String json = "{\"id\":\"new ID\",\"content\":[{\"id\":\"50e0300a-6668-42b3-a474-81a6a08f773f\"},{\"id\":\"f0bee3f3-2c40-4b44-8608-a6fedb226b7a\"}],\"data\":\"AAAAAAAA\"}";
        //when

        Container<Record> containerRecord = Container.deserialize(json, Record.class);
        //then
        assertEquals("50e0300a-6668-42b3-a474-81a6a08f773f", containerRecord.content.get(0).id);
        assertEquals("f0bee3f3-2c40-4b44-8608-a6fedb226b7a", containerRecord.content.get(1).id);
    }

    @Test
    public void testGenericWithType() {
        //given
        String json = "{\"id\":\"new ID\",\"content\":[{\"id\":\"50e0300a-6668-42b3-a474-81a6a08f773f\"},{\"id\":\"f0bee3f3-2c40-4b44-8608-a6fedb226b7a\"}],\"data\":\"AAAAAAAA\"}";
        //when
        Type type = new TypeToken<Container<Record>>() {
        }.getType();

        Container<Record> containerRecord = Container.deserializeType(json, type);
        //then
        assertEquals("50e0300a-6668-42b3-a474-81a6a08f773f", containerRecord.content.get(0).id);
        assertEquals("f0bee3f3-2c40-4b44-8608-a6fedb226b7a", containerRecord.content.get(1).id);
    }

    @Test
    public void testRecord() {
        //given
        String json = "{\"id\":\"new ID\",\"content\":[{\"id\":\"50e0300a-6668-42b3-a474-81a6a08f773f\"},{\"id\":\"f0bee3f3-2c40-4b44-8608-a6fedb226b7a\"}],\"data\":\"AAAAAAAA\"}";
        //when
        ContainerRecord containerRecord = new Gson().fromJson(json, ContainerRecord.class);
        //then
        assertEquals("50e0300a-6668-42b3-a474-81a6a08f773f", containerRecord.content.get(0).id);
        assertEquals("f0bee3f3-2c40-4b44-8608-a6fedb226b7a", containerRecord.content.get(1).id);
    }

    @Test
    public void testRecordWithType() {
        //given
        String json = "{\"id\":\"new ID\",\"content\":[{\"id\":\"50e0300a-6668-42b3-a474-81a6a08f773f\"},{\"id\":\"f0bee3f3-2c40-4b44-8608-a6fedb226b7a\"}],\"data\":\"AAAAAAAA\"}";
        //when
        Type type = new TypeToken<Container<Record>>() {
        }.getType();
        Container<Record> containerRecord = new Gson().fromJson(json, type);
        //then
        assertEquals("50e0300a-6668-42b3-a474-81a6a08f773f", containerRecord.content.get(0).id);
        assertEquals("f0bee3f3-2c40-4b44-8608-a6fedb226b7a", containerRecord.content.get(1).id);
    }

    @Test
    public void testContainerString() {
        //given
        String json = "{\"id\":\"new ID\",\"content\":[\"37c84304-ab80-4f92-8b2a-710b362ecb3f\"],\"data\":\"AAAAAAAA\"}";
        //when
        Type type = new TypeToken<Container<String>>() {
        }.getType();
        Container<String> containerRecord = new Gson().fromJson(json, type);
        //then
        assertEquals("37c84304-ab80-4f92-8b2a-710b362ecb3f", containerRecord.content.get(0));
    }


}

my class Record

public class Record {
    public String id;
}

and my Container

public class ContainerRecord extends Container<Record> {
}

and Container

public class Container<T> {

    public String id;
    public List<T> content;
    public Object data;

    public static <T> Container<T> deserializeClass(String json, Class<? extends Container<T>> type) {
        return new Gson().fromJson(json, type);
    }
    public static <T> Container<T> deserializeType(String json, Type type) {
        return new Gson().fromJson(json, type);
    }

    public static <T> Container<T> deserialize(String json, Class<T> clazz) {
        Type type = TypeToken.getParameterized(Container.class,clazz).getType();
        return new Gson().fromJson(json, type);
    }
}
DEV-Jacol
  • 567
  • 3
  • 10