2016-02-09 12 views
13

は、私は大体次のような構造親オブジェクトのparamsをGSONでデシリアライズして子オブジェクトをインスタンス化し、ジェネリックを使用しますか?

class MyDeserialParent<T extends MyChildInterface> { 

    MyChildInterface mSerialChild; 
    ... //some other fields (not 'type') 

} 

を持っている。しかし、次のように子供の二つの性質が親ノードに返される厄介なJSON構造からデシリアライズされます。

{ 
    "myDeserialParents" : [ 
     { 
      ... //some parent properties 
      "type": "value", //used in a TypeAdapter to choose child implementation 
      "childProp1": "1", 
      "childProp2": "2", 
     }, 
     ... //more in this list 
    ] 
} 

明らかにこれはただSerializedNameでmSerialChildに注釈を付けるとTypeAdapterはその魔法を働くせるから私を防ぎます。ですから、MyDeserialParentを逆シリアル化するときは、コンクリートクラスを見つけてMyChildInterfaceを探し、childProp1childProp2をコンストラクタのパラメータとして使用して新しいものを作ります。私はこれについてどうやって行くのか分かりません。

私はその後MyChildInterfaceのために自分を正しいコンクリートをインスタンス化し、MyDeserialParentためTypeAdapterJsonDeserializer)を使用して想像してdeserializeにタイプフィールド(だけでなく、2子プロパティ)を取得することができます。

これはつまりMyDeserialParentクラス(context.deserialize(json, MyDeserialParent.class))を作成し、MyChildInterfaceインスタンスのセッターを呼び出す必要があることを意味します。私は何かが欠けているようにそれは間違っていると感じる。より良い方法がありますか?

親オブジェクトも手動で作成すると、ジェネリック(:MyDeserialParent)を指定する方法もありますか?タイプ消去とは、これを行う方法がないことを意味しますか? (私はすでに代わりTを推測MyDeserialParentの特定のサブタイプを使用する場合、私は型の安全性を得ることができます知っているbecaseこの質問はそれほど重要ではないが、私はそれを避けたいのですが)あなたは、明らかにカスタムTypeAdapterが必要

答えて

3

。しかし、トリッキーな部分は、以下のとおりです。

  • あなたの親クラスは、一般的な1
  • mSerialChildはタイプTではないが、タイプMyChildInterface
  • の私たちは、それぞれの子クラスのために手でJSONをパース避けたいとコード全体を変更することなく、親にプロパティを追加することができます。

私は以下の解決策を思いつきました。

public class MyParentAdapter implements JsonDeserializer<MyDeserialParent>{ 

    private static Gson gson = new GsonBuilder().create(); 
    // here is the trick: keep a map between "type" and the typetoken of the actual child class 
    private static final Map<String, Type> CHILDREN_TO_TYPETOKEN; 

    static{ 
     // initialize the mapping once 
     CHILDREN_TO_TYPETOKEN = new TreeMap<>(); 
     CHILDREN_TO_TYPETOKEN.put("value", new TypeToken<MyChild1>(){}.getType()); 
    } 


    @Override 
    public MyDeserialParent deserialize(JsonElement json, Type t, JsonDeserializationContext 
      jsonDeserializationContext) throws JsonParseException{ 
     try{ 
      // first, get the parent 
      MyDeserialParent parent = gson.fromJson(json, MyDeserialParent.class); 
      // get the child using the type parameter 
      String type = ((JsonObject)json).get("type").getAsString(); 
      parent.mSerialChild = gson.fromJson(json, CHILDREN_TO_TYPETOKEN.get(type)); 
      return parent; 

     }catch(Exception e){ 
      e.printStackTrace(); 
     } 
     return null; 
    } 
} 

発言:

  • カスタムアダプターは、あなたがあなたの子供のためのいくつかのカスタムgsonプロパティが必要な場合、あなたはMyParentAdapterのコンストラクタでGsonオブジェクトを渡すことができgsonBuilder
  • に登録する必要があります現在はデフォルトのものを使用しています。
  • 子と親のには、別名の属性が必要です。;
  • 新しいタイプはすべて、対応するクラスのマップに追加する必要があります。

完全な例

メイン:

public class DeserializeExample{ 

    MyDeserialParent[] myDeserialParents; 

    static String json = "{\n" + 
      " \"myDeserialParents\" : [\n" + 
      "  {\n" + 
      "   \"otherProp\": \"lala\"," + 
      "   \"type\": \"value\", //used in a TypeAdapter to choose child implementation\n" + 
      "   \"childProp1\": \"1\",\n" + 
      "   \"childProp2\": \"2\"\n" + 
      "   }\n" + 
      "  ]\n" + 
      "}"; 


    public static void main(String[] args){ 
     Gson gson = new GsonBuilder().registerTypeAdapter(MyDeserialParent.class, new MyParentAdapter()).create(); 
     DeserializeExample result = gson.fromJson(json, DeserializeExample.class); 
     System.out.println(gson.toJson(result)); 
     // output: 
     // {"myDeserialParents":[{"mSerialChild":{"childProp1":"1","childProp2":"2"},"otherProp":"lala"}]} 
    }//end main 

}//end class 

親:

class MyDeserialParent<T extends MyChildInterface>{ 

    MyChildInterface mSerialChild; 
    //some other fields (not 'type') 
    String otherProp; 
} 

子:

public class MyChild1 implements MyChildInterface { 
    String childProp1; 
    String childProp2; 
}//end class 
+0

これは私自身の解決策に非常に近いもので、私が探していた解決策ではありませんが、ここに来るほとんどの人をカバーする良い答えではありません –

関連する問題