【问题标题】:Gson deserialize interface to its Class implementationGson 反序列化接口到它的类实现
【发布时间】:2016-06-28 08:56:47
【问题描述】:

我正在使用 Retrofit 2.1.0converter-gson:2.1.0 并单独使用 gson:2.6.2 以自定义序列化/反序列化。问题是我的 POJO 应该隐藏在接口后面,我想告诉 Gson 哪个类应该是反序列化的接口。并且在反序列化/序列化改造之后应该能够返回接口。如果我可以利用泛型并轻松创建一种方法来告诉 Gson 或 Retrofit 将 FooInterface 序列化/反序列化为 FooClass,那就太好了。

【问题讨论】:

    标签: java android gson retrofit2


    【解决方案1】:

    我假设您想为所有接口及其各自的实现创建一个反序列化器。请按照以下步骤操作:

    1. 创建一个基础接口,该接口将由您的其他应用接口扩展。需要为所有接口和实现类创建一个反序列化器。

    public interface Convertable {
         String getClassName();
    }
    

    2. 创建您的功能接口和实现类。例如,我们将它们命名为 FooInterface 和 FooClass。 FooInterface 应该扩展 Convertable 接口。

    FooInterface

    public interface FooInterface extends Convertable {
    
    }
    

    FooClass

    public class FooClass implements FooInterface {
    
        // DISCRIMINATOR FIELD
        private final String className;
    
        private String field1;
    
        private String field2;
    
        public FooClass() {
            this.className = getClass().getName();
        }
    
        public String getClassName() {
            return className;
        }
    
        public String getField1() {
            return field1;
        }
    
        public void setField1(String field1) {
            this.field1 = field1;
        }
    
        public String getField2() {
            return field2;
        }
    
        public void setField2(String field2) {
            this.field2 = field2;
        }
    
    }
    

    请注意,getClassName() 返回的值用作鉴别器字段,将在 Gson Deserializer(下一步)中用于初始化可返回实例。 我假设您的序列化器和反序列化器类将驻留在同一个包中,即使它们位于不同的客户端和服务器应用程序中。 如果不是,那么您将需要更改 getClassInstance() 实现,但会是这样做很简单。

    3.为您的所有应用程序实现自定义 Gson 序列化器

    import com.google.gson.JsonDeserializationContext;
    import com.google.gson.JsonDeserializer;
    import com.google.gson.JsonElement;
    import com.google.gson.JsonObject;
    import com.google.gson.JsonParseException;
    import com.google.gson.JsonPrimitive;
    
    public class ConvertableDeserializer<T extends Convertable> implements JsonDeserializer<T> {
    
        private static final String CLASSNAME = "className";
    
        public T deserialize(final JsonElement jsonElement, final Type type,
                             final JsonDeserializationContext deserializationContext
                            ) throws JsonParseException {
    
            final JsonObject jsonObject = jsonElement.getAsJsonObject();
            final JsonPrimitive prim = (JsonPrimitive) jsonObject.get(CLASSNAME);
            final String className = prim.getAsString();
            final Class<T> clazz = getClassInstance(className);
            return deserializationContext.deserialize(jsonObject, clazz);
        }
    
        @SuppressWarnings("unchecked")
        public Class<T> getClassInstance(String className) {
            try {
                return (Class<T>) Class.forName(className);
            } catch (ClassNotFoundException cnfe) {
                throw new JsonParseException(cnfe.getMessage());
            }
        }
    
    }
    

    4.向Gson注册Deserializer并初始化改造

     private static GsonConverterFactory buildGsonConverter() {
    
            final GsonBuilder builder = new GsonBuilder();
    
            // Adding custom deserializers
            builder.registerTypeAdapter(FooInterface.class, 
                                        new ConvertableDeserializer<FooInterface>());
            final Gson gson = builder.create();
    
            return GsonConverterFactory.create(myGson);
        }
    
    
        public void initRetrofit() {
            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl("REST_ENDPOINT")
                    .addConverterFactory(buildGsonConverter())
                    .client(httpClient)
                    .build();
        }
    

    如果需要,您可以为所有实现注册适配器,使用:

    builder.registerTypeAdapter(Convertable.class, new ConvertableDeserializer<Convertable>());
    

    【讨论】:

    • 我会检查这个实现并报告。
    • 我在ConvertableDeserializer 类的final String className = prim.getAsString(); 上检索NPE。这是显而易见的。我的 json 里面没有 className 字段。
    【解决方案2】:

    因为您愿意付出努力,使用接口几乎复制整个域层以隐藏模型的实现细节,我想您会发现我的回答令人耳目一新;)

    您应该使用AutoValue 来隐藏模型中的任何实现细节。它的工作方式非常简单:

    您编写一个抽象类,AutoValue 实现它。就这些 有它;实际上没有配置。

    采用这种方法,您将不需要创建如此多的样板。

    还有一个名为 auto-value-gson 的 AutoValue 扩展,它添加了开箱即用的 Gson De/Serializer 支持。

    通过这些简单的步骤,我认为您的代码库将大大改进。

    【讨论】:

    • 你能举个例子吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-13
    • 2011-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-04
    • 1970-01-01
    相关资源
    最近更新 更多