web-dev-qa-db-ja.com

Gson-フィールド値に基づく特定のオブジェクトタイプへの逆シリアル化

typeフィールドの値に基づいて、jsonオブジェクトを特定のタイプのオブジェクト(Gsonライブラリを使用)に逆シリアル化したい、たとえば:

[
    {
          "type": "type1",
          "id": "131481204101",
          "url": "http://something.com",
          "name": "BLAH BLAH",
          "icon": "SOME_STRING",
          "price": "FREE",
          "backgroundUrl": "SOME_STRING"
    },
    {
        ....
    }
]

したがって、typeフィールドは異なる(ただし既知の)値になります。その値に基づいて、jsonオブジェクトを適切なモデルオブジェクト(たとえば、Type1Model、Type2Modelなど)に逆シリアル化する必要があります。逆変換する前に、JSONArrayに変換することで簡単に実行できます。これを繰り返し、逆シリアル化する必要がある型を解決します。に。しかし、私はそれが醜いアプローチだと思います、そして私はより良い方法を探しています。助言がありますか?

20
lukaleli

JsonDeserializerを実装して、Json値をJavaインスタンスに解析するときに使用できます。アイデアを提供するコードで表示してみます:

1)着信json値のidプロパティによってクラスの異なるインスタンスを作成するカスタムJsonDeserializerクラスを定義します。

class MyTypeModelDeserializer implements JsonDeserializer<MyBaseTypeModel> {

    @Override
    public MyBaseTypeModel deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context)
            throws JsonParseException {

        JsonObject jsonObject = json.getAsJsonObject();

        JsonElement jsonType = jsonObject.get("type");
        String type = jsonType.getAsString();

        MyBaseTypeModel typeModel = null;     

        if("type1".equals(type)) {
            typeModel = new Type1Model();
        } else if("type2".equals(type)) {
            typeModel = new Type2Model();
        }
        // TODO : set properties of type model

        return typeModel;
    }
}

2)Javaオブジェクトの異なるインスタンスの基本クラスを定義します:

class  MyBaseTypeModel {
    private String type;
    // TODO : add other shared fields here
}

3)Javaオブジェクトのクラスの異なるインスタンスを定義して、基本クラスを拡張します:

class Type1Model extends MyBaseTypeModel {
    // TODO: add specific fields for this class
}

class Type2Model extends MyBaseTypeModel {
    // TODO: add specific fields for this class
}

4)これらのクラスを使用して、json値をBeanに解析します。

GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(MyBaseTypeModel.class, new MyTypeModelDeserializer());
Gson gson = gsonBuilder.create();

MyBaseTypeModel myTypeModel = gson.fromJson(myJsonString, MyBaseTypeModel.class);

私は今それをテストすることはできませんが、あなたがアイデアを得ることを望みます。また このリンク は非常に役立ちます。

31
Devrim

@ stephane-kの回答は機能しますが、少し混乱し、改善される可能性があります(彼の回答へのコメントを参照)

https://github.com/google/gson/blob/master/extras/src/main/Java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.Java をプロジェクトにコピーします。 (問題ありません。これらのクラスはコピー/貼り付けできるように設計されています https://github.com/google/gson/issues/845#issuecomment-217231315

セットアップモデルの継承:

// abstract is optional
abstract class BaseClass {
}

class Type1Model extends BaseClass {
}

class Type2Model extends BaseClass {
}

GSONをセットアップするか、既存のGSONを更新します。

RuntimeTypeAdapterFactory<BaseClass> typeAdapterFactory = RuntimeTypeAdapterFactory
        .of(BaseClass.class, "type")
        .registerSubtype(Type1Model.class, "type1")
        .registerSubtype(Type2Model.class, "type2");

Gson gson = new GsonBuilder().registerTypeAdapterFactory(typeAdapterFactory)
                .create();

JSONを基本クラスに逆シリアル化します。

String jsonString = ...
BaseClass baseInstance = gson.fromJson(jsonString, BaseClass.class);

baseInstanceはどちらかのinstanceofになりますType1ModelまたはType2Model

ここから、インターフェースにコーディングするか、instanceofをチェックしてキャストできます。

6
tir38

使用 https://github.com/google/gson/blob/master/extras/src/main/Java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.Java

次にそれを設定します

_public static final class JsonAdapterFactory extends 
    RuntimeTypeAdapterFactory<MediumSummaryInfo> {
        public JsonAdapterFactory() {
            super(MyBaseType.class, "type");
            registerSubtype(MySubtype1.class, "type1");
            registerSubtype(MySubtype2.class, "type2");
        }
}
_

そして注釈を追加します:

@JsonAdapter(MyBaseType.JsonAdapterFactory.class)

myBaseType

ずっといい。

2
stephane k.