Java – gson deserializes the interface of its class implementation
I'm using retrofit 2.1 0 and converter gson: 2.1 0 and gson alone: 2.6 2. Customize serialization / deserialization The problem is that my POJO should be hidden behind the interface. I want to tell gson which class should be a deserialized interface And after deserialization / serialization, the improvement should be able to return to the interface It would be a good thing if I could take advantage of generics and easily create a method to tell gson or retrofit to serialize / deserialize foointerface to fooclass
Solution
I assume you want to create a single deserializer for all interfaces and their respective implementations Please follow the steps below:
1. Create a basic interface that will be extended by other application interfaces A single deserializer needs to be created for all interfaces and implementation classes
public interface Convertable { String getClassName(); }
2. Create functional interfaces and implementation classes For example, let's name them foointerface and fooclass Foointerface should extend the 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 the discriminator field that will be used in the gson deserializer (next step) to initialize the returnable instance I assume that your serializer and deserializer classes will reside in the same package, even if they are in different client and server applications If not, you will need to change the getclassinstance () implementation, but this is very simple
3. Implement a custom gson serializer for all your applications
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 the transformation
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(); }
If necessary, you can register adapters for all implementations using:
builder.registerTypeAdapter(Convertable.class,new ConvertableDeserializer<Convertable>());