2015-11-22 73 views
1

我在編譯並且Android應用,它使用JSON與C#服務器進行通信。反序列化使用Gson進行派生類的基類列表

我有一個數據類被序列化(實際上,反序列化所接收到的數據轉換成),其中包含的派生類的列表字段,但將它們存儲作爲基類其中:

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

對於例如,從C#側如果我收到以下ToSerializeClass實例,稱爲serializeClass

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

JSON字符串將是:

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

「__type」:「SimpleClassName」 JSON字符串的字段顯示序列化類的簡單名稱。這些都是由C#端的序列化器添加的。 如果有必要,我可以讓這些字段消失,但這是C#方面同樣問題的解決方案。 沒有類型會是這樣的:

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

問題是,當我嘗試反序列化 JSON字符串到ToSerializeClass類的一個實例:

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

我有反序列化的類,它是一個ToSerializeClass實例,但是派生類列表是基類的集合,而不是派生類的派生類,所有派生的信息都會丟失。

如何將字符串反序列化爲ToSerializeClass實例具有派生類的列表?

我完全控制源代碼,所以我能夠修改我的數據類,如果需要使用不同的集合來創建一些包裝,修改JSON字符串,但我想解決它儘可能使用Gson,並且可以在沒有太多開銷的情況下完成。

謝謝!

編輯:例如DerivedClassADerivedClassB

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

    ... 

    public DerivedClassA(){ 
     super(); 
    } 
} 

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

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

    public DerivedClassB(){ 
     super(); 
    } 
} 

回答

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

SOLUTION: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

使用該班

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); 
+0

感謝您的回答,但這種方法看起來像我將不得不存儲所有的派生類屬性/領域進入一個 - 響應類。由於有很多不同的派生類,它將是一個巨大的「數據類」(有時與字段名稱等價)。我更喜歡這種方式 - 如果可能的話 - 將數據反序列化/反映到原始派生類。 (我知道,從響應類獲得必要的值後,我可以創建派生類,但是..這只是不好。):)如果我錯了你的例子,請隨時糾正我。 – KAI

+0

@KAI,你當然會想起BayBlade的。你的派生類將被嵌套在** json **列表中,所以你不必擔心Respose類的大小不會增加。如果您知道從服務器獲得的** JSON **的結構,那麼關於派生類實際上可以在您嘗試爲它們創建Model類時進行subclaased,非常方便 –

相關問題