从通用

时间:2016-05-12 07:11:52

标签: java android generics gson

我无法完全理解反射是如何工作的,所以我在尝试为GsonBuilder注册自定义类型适配器时遇到了一些麻烦。

我需要为List<Something>类型注册一个类型适配器。

这是一个有效的代码:

    gsonBuilder.registerTypeAdapter(
            new TypeToken<List<MyClass>>() { }.getType(), new ListDeserializer(MyClass.class)
    );

问题是我有很多类来注册我的类型适配器,而且我有一个ListDeserializerSingleDeserializer适配器。

我正在试图通用,所以我只是调用一个函数,传递我的GsonBuilder和类,然后你就去了。

这是我的尝试:

private static <T> void registerTypeAdapter(GsonBuilder gsonBuilder, T clazz) {
    gsonBuilder.registerTypeAdapter(clazz.getClass(), new SingleDeserializer(clazz.getClass()));
    gsonBuilder.registerTypeAdapter(
            new TypeToken<List<T>>() {
            }.getType(), new ListDeserializer(clazz.getClass())
    );
}

它对我的ListDeserializer无效。我的猜测是,由于TList的通用参数化类型,因此无法在运行时检测到它。

有什么建议吗?

1 个答案:

答案 0 :(得分:1)

是的,由于泛型type erasure,您无法在运行时使用new TypeToken<List<T>>() {}.getType()List<T>在运行时只是List<Object>。但你可以做的是创建ParameterizedType并使用类似的东西:

Type type = ParameterizedTypeImpl.make(List.class, new Type[]{clazz.getClass()}, null);
gsonBuilder.registerTypeAdapter(type, new ListDeserializer(clazz.getClass()) );

但你真的需要通用性吗?你可以做到

private static void registerTypeAdapter2(GsonBuilder gsonBuilder, Class<?> clazz) {
    gsonBuilder.registerTypeAdapter(clazz, new SingleDeserializer());
    Type type = ParameterizedTypeImpl.make(List.class, new Type[]{clazz}, null);
    gsonBuilder.registerTypeAdapter(type, new ListDeserializer());
}

以下是演示:

import com.google.gson.*;
import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) throws Exception {
        GsonBuilder gb = new GsonBuilder();
        registerTypeAdapter(gb, MyClass.class);

        Gson gson = gb.create();
        List data = new ArrayList<>();
        data.add(new MyClass("Bob"));
        data.add(new MyClass("Alice"));

        String listString = gson.toJson(data);
        String soloString = gson.toJson(new MyClass("Test"));

        Object resultList = gson.fromJson(listString, new TypeToken<List<MyClass>>() {}.getType());
        Object resultSolo = gson.fromJson(soloString, MyClass.class);

        System.out.println("RESULT:");
        System.out.println("FROM " + listString +" TO "+ resultList);
        System.out.println("FROM " + soloString +" TO "+ resultSolo);

    }

    private static void registerTypeAdapter(GsonBuilder gsonBuilder, Class<?> clazz) {
        gsonBuilder.registerTypeAdapter(clazz, new SingleDeserializer());
        Type type = ParameterizedTypeImpl.make(List.class, new Type[]{clazz}, null);
        gsonBuilder.registerTypeAdapter(type, new ListDeserializer());
    }

    private static class ListDeserializer implements JsonDeserializer {
        private static Gson gson = new Gson();

        @Override
        public Object deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            System.out.println("Used ListDeserializer:  Type " + typeOfT);

            Type t = (typeOfT instanceof ParameterizedType) ?
                    ((ParameterizedType) typeOfT).getActualTypeArguments()[0] :
                    Object.class;

            Type type = ParameterizedTypeImpl.make(List.class, new Type[]{t}, null);
            List list = gson.fromJson(json, type);
            return list;
        }
    }

    private static class SingleDeserializer implements JsonDeserializer {
        private static Gson gson = new Gson();

        @Override
        public Object deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            System.out.println("Used SingleDeserializer:  Type " + typeOfT);
            return gson.fromJson(json, typeOfT);
        }
    }

    public static class MyClass {
        @SerializedName("name")
        private String name;

        public MyClass(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "MyClass{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }
}