2016-08-19 6 views
0

以下は問題のドメインのクラス図です。異なるセマンティクスを持つJSONデコードメッセージがあります。これらのメッセージは、さまざまなビューに対してコード内で異なるメソッドを起動します(初期化、更新)。Gsonは多型メンバーを逆直列化しますリスト変数はnullです

Messageは、溶液を使用してInitMessageDataMessageいずれかに細かい非直列化されRuntimeTypeAdapterFactoryを使用し、可能なすべてのサブタイプを登録hereを提案しました。ただし、DataMessage.valueのリストは空です(デシリアライズされません)。問題は、DataMessageのネストされた多型メンバーです。

Class diagram of the problem domain

アダプタ工場:

RuntimeTypeAdapterFactory<Message> messageAdapterFactory = RuntimeTypeAdapterFactory 
    .of(Message.class, "MESSAGE_TYPE") 
    .registerSubtype(InitializationMessage.class, "INIT") 
    .registerSubtype(DataMessage.class, "DATA"); 

RuntimeTypeAdapterFactory<DataValue> dataAdapterFactory = RuntimeTypeAdapterFactory 
    .of(DataValue.class, "NAME") 
    .registerSubtype(DataValueA.class, "A") 
    .registerSubtype(DataValueB.class, "B") 
    .registerSubtype(DataValueC.class, "C"); 

メッセージの作成:

TypeToken<Message> typeToken = new TypeToken<Message>() {}; 
Message msg = gson.fromJson(json, typeToken.getType()); 

たDataMessageクラス:

public class DataMessage extends Message { 

    private List<DataValue> value; 

    public List<DataValue> getValue() { 
    return value; 
    } 

    public void setValue(List<DataValue> value) { 
    this.value= value; 
    } 
} 

D ataValueAクラス:

public class DataValueA extends DataValue { 

    private Map<String, Float> value; 

    public float getValue(String location) { 
    return value.get(location); 
    } 
} 

対応するJSON:

{ 
    "MESSAGE_TYPE" : "DATA", 
    "VALUE" : [ 
    { 
     "NAME" : "C", 
     "VALUE" : 1.3 
    }, 
    { 
     "NAME" : "A", 
     "VALUE" : { 
      "FL" : 18.4, 
      "FR" : 18.4, 
      "RL" : 18.4, 
      "RR" : 18.4 
     } 
    }] 
} 

私はDataValueは、それぞれのサブクラス(DataValueA ...)にデシリアライズされたいです。

+0

正しいサブクラスを作成し、 'contextを介してさらにデシリアライズを委譲する' Message'の 'JsonDeserializer'を書いてください。 deserialize(dataObject、DataValue.class) 'は、多型データを使用して多態性メッセージを逆シリアル化する機能を提供します。しかし、 'RuntimeTypeAdapterFactory'で提供されるデータバインディングの読み込み可能で保守可能なコードの利益を完全に失います。 – lschuetze

答えて

1

解決策は、JsonDeserializerを登録するためにGsonBuilder.registerTypeAdapterメソッドを使用することです。メッセージ内のフィールドを使用して、どのサブクラスが作成されるかを定義します(デフォルトで出荷されないRuntimeTypeAdapterFactoryのように、gson-extra)。

抽象スーパークラスごとにデシリアライザが登録されます。サブタイプを区別するために、フィールドを考えると

gson = new GsonBuilder() 
    .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) 
    .registerTypeAdapter(Message.class, new MessageAdapter()) 
    .registerTypeAdapter(DataValue.class, new DataValueAdapter()) 
    .create(); 

次のようにデシリアライズ機能を定義することができNAMEという名前です。フィールドの内容からそれぞれのサブクラスへのマッピングがあります。 (限り、あなたは標準デシリアライザで大丈夫ですとサブクラスのために使用されます)反射デシリアライザは、サブクラスがプロパティで@SerializedNameを述べる必要がある動作させるためには

public class DataValueAdapter implements JsonDeserializer<DataValue> { 
    private final static Map<String, Class<?>> FieldToClass; 

    static { 
    FieldToClass = new HashMap<>(); 
    FieldToClass.put("PERFORMANCE", PerformanceDataValue.class); 
    FieldToClass.put("TIRE_SLIP", TireSlipDataValue.class); 
    } 

    @Override 
    public DataValue deserialize(JsonElement json, Type typeOfT, 
           JsonDeserializationContext context) throws JsonParseException { 
    JsonObject jsonObject = json.getAsJsonObject(); 
    String dataType = jsonObject.get("NAME").getAsString(); 
    return context.deserialize(json, FieldToClass.get(dataType)); 
    } 
} 

。それがなければ私のために働かなかった。

関連する問題