2

I want to deserialize an array of complex objects

for instance i have object MyTestObject

public class MyTestObject{

    String param1;
    String param2; 
    int param3;
}

now i have an array of objects named MyTestObject

and my json looks like this

{
[
"param1":"something1",
"param2:"something2",
"param3":3
],
[
"param1":"something1",
"param2:"something2",
"param3":3
],
[
"param1":"something1",
"param2:"something2",
"param3":3
]
}

this is my code for desearilization

public static <T> List<T> fromJSON(String json, Class<T> className) {
    GsonBuilder builder = new GsonBuilder();
    builder.serializeNulls();
    Type myType = new TypeToken<List<T>>() {
    }.getType();
    Gson g1 = builder.create();
    List<T> objects = (List<T>) g1.fromJson(json, (Type) myType);
    for (T object : objects) {
        Log.d(TAG, "object: " + object + " class " + object.getClass());
    }
    return objects;
}

ArrayList<MyTestObject> objects = fromJSON(myjson,MyTestObject.class);

the array itself gets deserialized correctly, however, the objects inside it are not of the correct class, they are of type com.google.gson.internal.LinkedTreeMap

when i need them to be of type MyTestObject

how do i fix this?

Ashish Aggarwal
  • 3,018
  • 2
  • 23
  • 46
Lena Bru
  • 13,521
  • 11
  • 61
  • 126
  • this might help http://stackoverflow.com/questions/17231298/serializing-a-sparsearrayt-with-gson – Tala Aug 05 '13 at 09:48
  • 1
    That's not valid JSON! You can't have this `{[...], [...], [...]}`, but you need this `{'foo':[...], 'bar':[...], 'baz':[...]}`... I mean, the arrays within the root object must have a name... – MikO Aug 05 '13 at 12:00

2 Answers2

3

A sample :

public class Test<T> {
  public static void main(String[] args) throws Exception {
    new Test<String>().test(String.class);
    new Test<Integer>().test(Integer.class);
    new Test<Long>().test(Long.class);
  }

  public void test(Class<T> cls) throws Exception {
    TypeToken<?> typeOfT = getGenToken(List.class, cls);
    List<?> lst = (List<?>) new Gson().fromJson("[1, 2, 3]", typeOfT.getType());

    for (Object o : lst) {
      System.out.println("value : " + o + ", type : " + o.getClass());
    }
  }

  static TypeToken<?> getGenToken(final Class<?> raw, final Class<?> gen) throws Exception {
    Constructor<ParameterizedTypeImpl> constr = ParameterizedTypeImpl.class.getDeclaredConstructor(Class.class, Type[].class, Type.class);
    constr.setAccessible(true);
    ParameterizedTypeImpl paramType = constr.newInstance(raw, new Type[] { gen }, null);

    return TypeToken.get(paramType);
  }
}

ParameterizedTypeImpl came from sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl.

Output :

value : 1, type : class java.lang.String
value : 2, type : class java.lang.String
value : 3, type : class java.lang.String
value : 1, type : class java.lang.Integer
value : 2, type : class java.lang.Integer
value : 3, type : class java.lang.Integer
value : 1, type : class java.lang.Long
value : 2, type : class java.lang.Long
value : 3, type : class java.lang.Long

Note : in fact we could replace the wildcar with T, but that's not usefull here.

PomPom
  • 1,468
  • 11
  • 20
  • hm, android does not have Oracle JVM functions, do you have a different way to do `ParameterizedTypeImpl` ? – CQM Feb 06 '14 at 20:57
  • you can try to make your own implementation of `ParameterizedTypeImpl`, simply copy/paste the code of the Sun class, you can google the source easily – PomPom Feb 11 '14 at 21:01
1

You can't use new TypeToken<List<T>>(), because of type erasure GSon won't know the type of T. https://sites.google.com/site/gson/gson-user-guide#TOC-Serializing-and-Deserializing-Generic-Types

You can either provide in a static way Tlike

Type myType = new TypeToken<List<Something>>() {
}.getType();

Or build a TypeToken with a Class<T> var for the generic. There is no simple way to do this as far as I know. This is how I do it :

 static TypeToken<?> getGenToken(final Class<?> raw, final Class<?> gen) throws Exception {
    Constructor<ParameterizedTypeImpl> constr = ParameterizedTypeImpl.class.getDeclaredConstructor(Class.class, Type[].class, Type.class);
    constr.setAccessible(true);
    ParameterizedTypeImpl paramType = constr.newInstance(raw, new Type[] { gen }, null);

    return TypeToken.get(paramType);
  }

Then you can build the token like this :

Class<T> tClass = ...;
Type myType = new getGenToken<List.class, tClass>()
PomPom
  • 1,468
  • 11
  • 20
  • I get noSuchMethodException for that on the getDeclaredConstructor – Lena Bru Aug 05 '13 at 14:14
  • Strange. Which Gson version ? What constructor(s) do you see ? `System.out.println(Arrays.toString(ParameterizedTypeImpl.class.getDeclaredConstructors()));` – PomPom Aug 05 '13 at 14:29
  • Also the method I provided simply allow to return an instance of a `ParameterizedType`. If you want you can extract `ParameterizedTypeImpl` (which is static private final) located in `$Gson$Types` and make it public. – PomPom Aug 05 '13 at 15:50
  • can you give me a working example please? i am battling with this for 2 days now , i have a josn arry filled with json objects all of the same type, if i deserialize each object individually, they are fine, if i try to extract them from the array... there goes the problem, strangely enough, there is a json array as one of the parameters in the object, that is of a difference class, and it gets deserialized just fine, this is why i thought this may work too, but i don't understand why it doesnt – Lena Bru Aug 05 '13 at 18:46