java 使用 Gson 和抽象类

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16000163/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 21:35:51  来源:igfitidea点击:

Using Gson and Abstract Classes

javaobjectgsonabstract-class

提问by André Carvalho

I'm trying to exchange messages between a client and a server using GSON.

我正在尝试使用 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 hereand here, but none of them solves the problem.

我在这里这里找到了 2 个教程,但没有一个能解决问题。

Edit1:

编辑1:

The class Message is this one:

Message 类是这样的:



public class Mensagem
{
private TipoMensagem    type;

    private Conteudo        conteudo;

    private Cliente         autor;
    private Cliente         destino;    // null -> to all(broadcast)
}


And Content is this one:

内容是这样的:

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

And an example of a extend class from contedo is this one:

来自 contedo 的扩展类的一个例子是这个:

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;

}



}


Finaly what i do is like : "String strObject = new Gson().toJson(mensage);" which works but on deserialization it doesnt because it assumes always that it is from Content class

最后我所做的是:“String strObject = new Gson().toJson(mensage);” 它有效,但在反序列化时它不起作用,因为它始终假定它来自 Content 类

回答by André Carvalho

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());"

注意:“registerTypeAdapter( AbstractClass.class, new InterfaceAdapter());”

by AbstractClass.classi mean the class that you are implementing in my case it was Conteúdo that could be ConteudoTweet or ConteudoUserSystem and so on...

通过AbstractClass.class我的意思是你在我的例子中实现的类是 Conteúdo,可能是 ConteudoTweet 或 ConteudoUserSystem 等等......

The implementation of InterfaceAdapteris :

InterfaceAdapter的实现是:

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

这个 InterfaceAdapter 是通用的,所以它应该可以正常工作......

That's it!

而已!

回答by rpax

You should take a look on a similar question I've answered here : https://stackoverflow.com/a/22081826/3315914

你应该看看我在这里回答的类似问题:https: //stackoverflow.com/a/22081826/3315914

You need to use Gson's RuntimeTypeAdapterFactory

你需要使用Gson 的 RuntimeTypeAdapterFactory

And register the base class and all child classes to make it work.

并注册基类和所有子类以使其工作。