I'm trying to exchange messages between a client and a server using GSON.
The problem is the following:
I have this structure:
public class Message 
{
    private TypeOfContent type; //  It's a enum 
    private Content       content;
    ....
}
Then the object content can be a various set of Classes.
I found 2 tutorials here and here, but none of them solves the problem.
Edit1:
public class Mensagem
{
    private TipoMensagem    type;
    private Conteudo        conteudo;
    private Cliente         autor;
    private Cliente         destino;    // null -> to all(broadcast)
}
public class Conteudo
{
    protected   TipoConteudo typeConteudo; 
    protected   String      texto;
    protected   Posicao     posicao;
    public Conteudo(TipoConteudo typeConteudo, String texto, Posicao posicao)
    {
        this.texto   = texto;
        this.posicao = posicao;  
        this.typeConteudo = typeConteudo;
    }    
} 
public class ConteudoTweet extends Conteudo 
{
    protected   String      pathImagem;
    public ConteudoTweet(TipoConteudo typeConteudo, String tweet, Posicao location, String picturePath) 
    {
        super(typeConteudo,tweet, location);
    
        this.pathImagem = picturePath;
    }
}
Finally what I do is like : "String strObject = new Gson().toJson(mensage);" which works but on deserialization it doesn't because it assumes always that it is from Content class
GSON can use the Object definition to directly create an object of the desired type. While JSONObject needs to be parsed manually.
By default, GSON excludes transient and static fields from the serialization/deserialization process.
Gson Goals: Provide simple toJson() and fromJson() methods to convert Java objects to JSON and vice-versa. Allow pre-existing unmodifiable objects to be converted to and from JSON. Extensive support of Java Generics.
Gson requires the class to have a default no-args constructor.
I finally solved it!
    // GSON
    GsonBuilder gsonBilder = new GsonBuilder();
    gsonBilder.registerTypeAdapter(Conteudo.class, new InterfaceAdapter<Conteudo>());
    gsonBilder.setPrettyPrinting();
    Gson gson =gsonBilder.create();
    String str2send = gson.toJson(message);
    Mensagem msg_recv = gson.fromJson(str2send,Mensagem.class);
Note that: "registerTypeAdapter(AbstractClass.class, new InterfaceAdapter());"
by AbstractClass.class i mean the class that you are implementing in my case it was Conteúdo that could be ConteudoTweet or ConteudoUserSystem and so on...
The implementation of InterfaceAdapter is :
import java.lang.reflect.Type;
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.JsonSerializationContext;
import com.google.gson.JsonSerializer;
public class InterfaceAdapter<T>
    implements JsonSerializer<T>, JsonDeserializer<T> {
    @Override
    public final JsonElement serialize(final T object, final Type interfaceType, final JsonSerializationContext context) 
    {
        final JsonObject member = new JsonObject();
        member.addProperty("type", object.getClass().getName());
        member.add("data", context.serialize(object));
        return member;
    }
    @Override
    public final T deserialize(final JsonElement elem, final Type interfaceType, final JsonDeserializationContext context) 
            throws JsonParseException 
    {
        final JsonObject member = (JsonObject) elem;
        final JsonElement typeString = get(member, "type");
        final JsonElement data = get(member, "data");
        final Type actualType = typeForName(typeString);
        return context.deserialize(data, actualType);
    }
    private Type typeForName(final JsonElement typeElem) 
    {
        try 
        {
            return Class.forName(typeElem.getAsString());
        } 
        catch (ClassNotFoundException e) 
        {
            throw new JsonParseException(e);
        }
    }
    private JsonElement get(final JsonObject wrapper, final String memberName) 
    {
        final JsonElement elem = wrapper.get(memberName);
        if (elem == null) 
        {
            throw new JsonParseException(
                "no '" + memberName + "' member found in json file.");
        }
        return elem;
    }
}
And this InterfaceAdapter is generic so it should work in general...
That's it!
You should take a look on a similar question I've answered here : https://stackoverflow.com/a/22081826/3315914
You need to use Gson's RuntimeTypeAdapterFactory
And register the base class and all child classes to make it work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With