Gson deserialize interface to its Class implementation

后端 未结 2 1284
甜味超标
甜味超标 2021-02-14 20:35

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/deserial

2条回答
  •  伪装坚强ぢ
    2021-02-14 21:01

    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. 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. As an example, lets name them FooInterface and FooClass. FooInterface should extend Convertable interface.

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

    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. 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.

    3. Implement a custom Gson Serializer for all of your application

    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 implements JsonDeserializer {
    
        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 clazz = getClassInstance(className);
            return deserializationContext.deserialize(jsonObject, clazz);
        }
    
        @SuppressWarnings("unchecked")
        public Class getClassInstance(String className) {
            try {
                return (Class) Class.forName(className);
            } catch (ClassNotFoundException cnfe) {
                throw new JsonParseException(cnfe.getMessage());
            }
        }
    
    }
    

    4. Register Deserializer with Gson and initialize retrofit

     private static GsonConverterFactory buildGsonConverter() {
    
            final GsonBuilder builder = new GsonBuilder();
    
            // Adding custom deserializers
            builder.registerTypeAdapter(FooInterface.class, 
                                        new ConvertableDeserializer());
            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());
    

提交回复
热议问题