简体   繁体   English

Gson将接口反序列化为其Class实现

[英]Gson deserialize interface to its Class implementation

I am using Retrofit 2.1.0 with converter-gson:2.1.0 and separately gson:2.6.2 in order to customize the serialization/deserialization. 我正在使用Retrofit 2.1.0converter-gson:2.1.0以及单独使用gson:2.6.2来自定义序列化/反序列化。 The problem is that my POJOs should be hidden behind interfaces and I want to tell Gson which class should be the deserialized interface. 问题是我的POJO应该隐藏在接口后面,我想告诉Gson哪个类应该是反序列化的接口。 And after the deserialization/ serialization Retrofit should be able to return the interface. 在反序列化/序列化之后,Retrofit应该能够返回接口。 It would be good if I can take advantage of Generics and easily create a way to tell Gson or Retrofit to serialize/deserialize FooInterface to FooClass. 如果我可以利用泛型并轻松创建一种方法来告诉Gson或Retrofit将FooInterface序列化/反序列化为FooClass,那将是一件好事。

I am assuming that you want to create a single deserializer for all of your interfaces and their respective implementations. 我假设您要为所有接口及其各自的实现创建单个反序列化器。 Follow these steps please: 请按以下步骤操作:

1. Create a base interface that will be extended by your other app interfaces. 1.创建一个将由其他应用程序界面扩展的基本界面。 It is required to create a single deserializer for all of your interfaces and implementation classes. 需要为所有接口和实现类创建单个反序列化器。

public interface Convertable {
     String getClassName();
}

2. Create your feature interface and implementation class. 2.创建功能界面和实现类。 As an example, lets name them FooInterface and FooClass. 例如,让我们将它们命名为FooInterface和FooClass。 FooInterface should extend Convertable interface. FooInterface应该扩展Convertable接口。

FooInterface FooInterface

public interface FooInterface extends Convertable {

}

FooClass 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;
    }

}

Note that the value returned by getClassName() is used as discriminator field that will be used in Gson Deserializer (next step) to initialize returnable instance. 请注意,getClassName()返回的值用作将在Gson反序列化器(下一步)中用于初始化可返回实例的鉴别器字段。 I am assuming that your serializer and deserializer class will reside in the same package even if they are in different client and server applications. 我假设您的序列化程序和反序列化程序类将驻留在同一个程序包中,即使它们位于不同的客户端和服务器应用程序中。 If not, then you will need to change getClassInstance() implementation, but would be pretty simple to do so. 如果没有,那么您将需要更改getClassInstance()实现,但这样做非常简单。

3. Implement a custom Gson Serializer for all of your application 3.为您的所有应用程序实现自定义Gson Serializer

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. Register Deserializer with Gson and initialize retrofit 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();
    }

You may register the adapter for all of your implementations if you want, using: 如果需要,您可以为所有实现注册适配器,使用:

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

Because you are willing to take the effort to almost duplicate your entire domain layer using interfaces to hide the implementation detail of your models, I think you will find my answer refresing ;) 因为您愿意使用接口来隐藏模型的实现细节,所以我愿意努力几乎复制您的整个域层,我想您会找到我的答案刷新;)

You should use AutoValue in order to hide any implementation detail in your models. 您应该使用AutoValue来隐藏模型中的任何实现细节。 The way it works is pretty simple: 它的工作方式非常简单:

You write an abstract class, and AutoValue implements it. 您编写了一个抽象类,AutoValue实现了它。 That is all there is to it; 这就是它的全部; there is literally no configuration. 实际上没有配置。

Adopting this approach you won't need to create such amount of boilerplate. 采用这种方法,您不需要创建这样的样板量。

And there is this AutoValue Extension called auto-value-gson , which adds Gson De/Serializer support out of box. 还有一个名为auto-value-gson的 AutoValue扩展,它可以在开箱即用时添加Gson De / Serializer支持。

With these simple steps I think your code base will improve substantially. 通过这些简单的步骤,我认为您的代码库将大大改善。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM