使用Gson将基类列表反序列化为派生类

时间:2015-11-22 16:21:53

标签: java android serialization

我正在构建 Android应用,它使用JSON与C#服务器进行通信。

我有一个要序列化的数据类(实际上,要将接收到的数据反序列化),它包含派生类的List字段,但将它们存储为它们的基类:

public class shortpa {

    public int distance;
    public int parentVert;
    public char label;
    public boolean isInTree;
    public final int MAX_VERTS=20;
    public final int INFINITY=1000000;
    public Vertex vertexList[];
    public int adjMat[][];
    public int nVerts;
    public int nTree;
    public DistPar sPath[];
    public int currentVert;
    public int startToCurrent;


    public static void main(String[] args)
    {
        Graph theGraph = new Graph();//error here saying graph can nt be resolved to a type
        theGraph.addVertex('A');
        theGraph.addVertex('B');
        theGraph.addVertex('C');
        theGraph.addVertex('D');
        theGraph.addVertex('Z');


        theGraph.addEdge(1, 2, 10);//1 repesents A, 2 repesents B....etc and 10 is the weight
        theGraph.addEdge(1, 5, 18);
        theGraph.addEdge(2, 3, 3);
        theGraph.addEdge(2, 5,17);
        theGraph.addEdge(2, 4, 1);
        theGraph.addEdge(3, 4, 1);
        theGraph.addEdge(4, 5, 4);

        System.out.println("Shortest paths");
        theGraph.path();
        System.out.println();

    }
//--------------------------------------------------------------------------    
    public void DistPar(int pv, int d)
    {
        distance = d;
        parentVert=pv;
    }
//--------------------------------------------------------------------------    
    public void Vertex(char lab)
    {
        label = lab;
        isInTree = false;
    }
//--------------------------------------------------------------------------    
    public void Graph()
    {
        vertexList = new Vertex[MAX_VERTS];/////error here saying vertex can not be resolved to a type
        adjMat = new int[MAX_VERTS][MAX_VERTS];
        nVerts=0;
        nTree=0;
        for(int j =0; j<MAX_VERTS; j++)
            for(int k=0; k<MAX_VERTS; k++)
            adjMat[j][k] = INFINITY;
        sPath = new DistPar[MAX_VERTS];//error here saying distpar can not be resolved to a type
    }
//--------------------------------------------------------------------------
    public void addVertex(char lab)
    {
        vertexList[nVerts++] = new Vertex(lab);
    }
//-------------------------------------------------------------------------
    public void addEdge(int start, int end, int weight)////have a look here
    {
        adjMat[start][end]=weight;
    }
//--------------------------------------------------------------------
    public void path()
    {
        int startTree = 0;
        vertexList[startTree].isInTree = true;
        nTree = 1;

        for(int j=0; j<nVerts; j++)
        {
            int tempDist = adjMat[startTree][j];
            sPath[j] = new DistPar(startTree, tempDist);
        }
        while(nTree < nVerts)
        {
            int indexMin = getMin();
            int minDist = sPath[indexMin].distance;

            if(minDist == INFINITY)
            {
                System.out.println("Unreachable vertexs");
                break;
            }
            else
            {
                currentVert=indexMin;
                startToCurrent=sPath[indexMin].distance;
            }
            vertexList[currentVert].isInTree = true;
            nTree++;
            adjust_sPath();
        }
        displayPaths();
        nTree = 0;
        for(int j=0; j<nVerts; j++)
            vertexList[j].isInTree = false;
    }
//---------------------------------------------------------------------------   
    public int getMin()
    {
        int minDist = INFINITY;
        int indexMin = 0;
        for(int j=1; j<nVerts;j++)
        {
            if(!vertexList[j].isInTree && sPath[j].distance < minDist)
            {
                minDist = sPath[j].distance;
                indexMin = j;
            }
        }
        return indexMin;
    }
//------------------------------------------------------------------------------
    public void adjust_sPath()
    {
        int column = 1;
        while(column < nVerts)
        {
            if(vertexList[column].isInTree)
            {
                column++;
                continue;
            }
            int currentToFringe = adjMat[currentVert][column];
            int startToFringe = startToCurrent + currentToFringe;
            int sPathDist = sPath[column].distance;
            if(startToFringe < sPathDist)
            {
                sPath[column].parentVert = currentVert;
                sPath[column].distance = startToFringe;
            }
            column++;
        }
    }
//------------------------------------------------------------------------------
    public void displayPaths()
    {
        for(int j=0; j< nVerts; j++)
        {
            System.out.println(vertexList[j].label+"=");
            if(sPath[j].distance == INFINITY)
                System.out.println("inf");
            else
                System.out.println(sPath[j].distance);
            char parent = vertexList[sPath[j].parentVert].label;
            System.out.println(" (" + parent + ") ");
        }
        System.out.println("");
    }


}

例如,如果我正在接收以下ToSerializeClass实例,来自C#端,名为 serializeClass

public class ToSerializeClass{
    @SerializedName("TestString")
    private String testString = "TestStringValue";

    @SerializedName("DerivedClasses")
    private List<BaseClass> derivedClasses;

    public List<BaseClass> getDerivedClasses() {
        return derivedClasses;
    }

    public ToSerializeClass(List<BaseClass> derivedClasses){
        this.derivedClasses= derivedClasses;
    }
}

JSON字符串将是:

List<BaseClass> derivedClasses = new ArrayList<>();
derivedClasses.add(new DerivedClassA());
derivedClasses.add(new DerivedClassB());
ToSerializeClass serializeClass = new ToSerializeClass(derivedClasses);

JSON字符串的&#34; __ type&#34;:#34; SimpleClassName&#34; 字段显示序列化类的简单名称。这些是由C#side的序列化程序添加的。 如果有必要的话,我可以让这些字段消失,但这是C#方面同样问题的解决方案。 没有类型,它看起来像这样:

{"__type":"ToSerializeClass","DerivedClasses":[{"__type":"DerivedA","FieldA":"This is a derived class."},{"__type":"DerivedB","FieldB":"This is ANOTHER derived class.", "IntValue":10}],"TestString":"TestStringValue"}

问题是,当我尝试反序列化 JSON字符串到 ToSerializeClass 类的实例时:

{"DerivedClasses":[{"FieldA":"This is a derived class."},{"FieldB":"This is ANOTHER derived class.", "IntValue":10}],"TestString":"TestStringValue"}

我有反序列化的类,它是 ToSerializeClass 实例,但 derivedClasses List是基类而不是派生类的集合,并且所有派生的信息都会丢失。

如何将String反序列化为 ToSerializeClass 实例以获得派生类的列表?

我可以完全控制源代码,因此我可以修改我的数据类,在必要时使用不同的集合来创建一些包装器,修改JSON字符串,但是我可以喜欢尽可能使用Gson解决它,并且可以在没有太多开销的情况下完成。

谢谢!

编辑,例如 DerivedClassA DerivedClassB

Gson serializer = new Gson();
ToSerializeClass deserialized = serializer.fromJson(jsonString, ToSerializeClass.class);

2 个答案:

答案 0 :(得分:0)

我已经创建了一个快速的解决方案,它接近我正在寻找的解决方案(我不是一个Java人员,所以如果它伤害了你的大脑,我很抱歉,随时评论建议)。

数据类:

public class BaseClass {
    @SerializedName("Method")
    private String method;

    public void setMethod(String method){
        this.method = method;
    }

    public String getMethod(){
        return method;
    }

    public BaseClass(String method){
        this.method = method;
    }
}

public class DerivedClassA extends BaseClass{
    @SerializedName("FieldA")
    private String fieldA = "This is a derived class.";

    public DerivedClassA(){
        super("ClassA");
    }
}

public class DerivedClassB extends BaseClass{
    @SerializedName("FieldB")
    private String fieldB = "This is ANOTHER derived class.";

    @SerializedName("IntValue")
    private int intValue = 10;

    public DerivedClassB(){
        super("ClassB");
    }
}

public class ToSerializeClass{
    @SerializedName("TestString")
    private String testString = "TestStringValue";

    @SerializedName("DerivedClasses")
    private List<BaseClass> derivedClasses;

    public ToSerializeClass(List<BaseClass> derivedClasses){
        this.derivedClasses= derivedClasses;
    }
}

解决方案: ClassDeserializerAdapter 实施:

public class ClassDeserializerAdapter implements JsonDeserializer<BaseClass>
{
    private String typeName;
    private Gson gson;
    private Map<String, Class<? extends BaseClass>> classTypeRegistry;

    ClassDeserializerAdapter(String typeName)
    {
        this.typeName = typeName;
        gson = new Gson();
        classTypeRegistry = new HashMap<>();
    }

    void registerClassType(String classTypeName, Class<? extends BaseClass> classType)
    {
        // registering Types to Strings
        classTypeRegistry.put(classTypeName, classType);
    }

    @Override
    public BaseClass deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) 
        throws JsonParseException
    {
        JsonObject jsonObject = json.getAsJsonObject();
        JsonElement typeElement = jsonObject.get(typeName);
        String method = typeElement.getAsString();
        Class<? extends BaseClass> classType = classTypeRegistry.get(method);
        BaseClass result = gson.fromJson(json, classType);
        return result;
    }
}

这对我来说很好,甚至没有临时课程等。

注意:通过反射,它可以自动注册从BaseClass继承的每个类,您甚至不必处理注册过程。

示例: main()方法的正文是:

// **SERIALIZATION PART** (nothing special, simple Gson serialization)
// Creating a list to pass as parameter to the container class
List<BaseClass> derivedClasses = new ArrayList<>();
derivedClasses.add(new DerivedClassA());
derivedClasses.add(new DerivedClassB());
// Creating the container class to be serialized
ToSerializeClass serializeClass = new ToSerializeClass(derivedClasses);

Gson gson = new Gson();

String json = gson.toJson(serializeClass);
// json = {"TestString":"TestStringValue","DerivedClasses":[{"FieldA":"This is a derived class.","Method":"ClassA"},{"FieldB":"This is ANOTHER derived class.","IntValue":10,"Method":"ClassB"}]}


// **DESERIALIZATION PART** (with custom deserializer)
// creating the custom deserializer, which will find the derived class' type as the class' "Method" field value. With that value, it can resolve the type.. see below
ClassDeserializerAdapter deserializer = new ClassDeserializerAdapter("Method");
// registering each Type into the Deserializer's HashMap (key-value pair), where the key (String) must be carried by the object (you can find it in the BaseClass, called "Method")
deserializer.registerClassType("ClassA", DerivedClassA.class);
deserializer.registerClassType("ClassB", DerivedClassB.class);
Gson gsonB = new GsonBuilder().registerTypeAdapter(BaseClass.class, deserializer).create();

// deserializing
ToSerializeClass deserialized = gsonB.fromJson(json, ToSerializeClass.class); // CORRECT!

答案 1 :(得分:-1)

使用此类

import com.google.gson.annotations.SerializedName;

public class DerivedClass {

    @SerializedName("__type")
    private String __type;

    @SerializedName("FieldA")
    private String fieldA;

    @SerializedName("FieldB")
    private String fieldB;

    public String get__type() {
        return __type;
    }

    public void set__type(String __type) {
        this.__type = __type;
    }

    public String getFieldA() {
        return fieldA;
    }

    public void setFieldA(String fieldA) {
        this.fieldA = fieldA;
    }

    public String getFieldB() {
        return fieldB;
    }

    public void setFieldB(String fieldB) {
        this.fieldB = fieldB;
    }
}

Response.java获得JSON数据

import java.util.ArrayList;

import com.google.gson.annotations.SerializedName;

public class Response {

    @SerializedName("__type")
    private String __type;

    @SerializedName("DerivedClasses")
    private ArrayList<DerivedClass> derivedClasses;

    @SerializedName("TestString")
    private String testString;

    public String get__type() {
        return __type;
    }

    public void set__type(String __type) {
        this.__type = __type;
    }

    public ArrayList<DerivedClass> getDerivedClasses() {
        return derivedClasses;
    }

    public void setDerivedClasses(ArrayList<DerivedClass> derivedClasses) {
        this.derivedClasses = derivedClasses;
    }

    public String getTestString() {
        return testString;
    }

    public void setTestString(String testString) {
        this.testString = testString;
    }
}

现在您所要做的就是使用此行,您将获得数据

    Response response = (new Gson()).fromJson("your_JSON", Response.class);