web-dev-qa-db-ja.com

Gsonで抽象クラスを逆シリアル化する

Gsonで逆シリアル化しようとしているJSON形式のツリーオブジェクトがあります。各ノードには、オブジェクトタイプNodeのフィールドとして子ノードが含まれます。 Nodeは、いくつかの具象クラス実装を持つインターフェースです。逆シリアル化プロセス中に、ノードを逆シリアル化するときに実装する具体的なクラスをGsonにどのように通信できますか?ノードが属しているタイプ?各Nodeには、タイプを指定するメンバーフィールドがあります。オブジェクトがシリアル化された形式のときにフィールドにアクセスし、何らかの方法でタイプをGsonに通知する方法はありますか?

ありがとう!

40
user437480

Nodesにカスタム JsonDeserializer を追加することをお勧めします。

Gson gson = new GsonBuilder()
    .registerTypeAdapter(Node.class, new NodeDeserializer())
    .create();

デシリアライザのメソッドでノードを表すJsonElementにアクセスし、それをJsonObjectに変換して、タイプを指定するフィールドを取得できます。その後、それに基づいて正しいタイプのNodeのインスタンスを作成できます。

47
ColinD

JSONSerializerとJSONDeserializerの両方を登録する必要があります。また、次の方法ですべてのインターフェースに汎用アダプターを実装できます。

  • シリアライゼーション中:実際のimplクラスタイプのMETA情報を追加します。
  • DeSerialization中:そのメタ情報を取得し、そのクラスのJSONDeserailizeを呼び出します

これは私が自分で使用した実装で、正常に動作します。

public class PropertyBasedInterfaceMarshal implements
        JsonSerializer<Object>, JsonDeserializer<Object> {

    private static final String CLASS_META_KEY = "CLASS_META_KEY";

    @Override
    public Object deserialize(JsonElement jsonElement, Type type,
            JsonDeserializationContext jsonDeserializationContext)
            throws JsonParseException {
        JsonObject jsonObj = jsonElement.getAsJsonObject();
        String className = jsonObj.get(CLASS_META_KEY).getAsString();
        try {
            Class<?> clz = Class.forName(className);
            return jsonDeserializationContext.deserialize(jsonElement, clz);
        } catch (ClassNotFoundException e) {
            throw new JsonParseException(e);
        }
    }

    @Override
    public JsonElement serialize(Object object, Type type,
            JsonSerializationContext jsonSerializationContext) {
        JsonElement jsonEle = jsonSerializationContext.serialize(object, object.getClass());
        jsonEle.getAsJsonObject().addProperty(CLASS_META_KEY,
                object.getClass().getCanonicalName());
        return jsonEle;
    }

}

次に、このアダプタをすべてのインターフェースに次のように登録できます

Gson gson = new GsonBuilder()
        .registerTypeAdapter(IInterfaceOne.class,
                new PropertyBasedInterfaceMarshal())
        .registerTypeAdapter(IInterfaceTwo.class,
                new PropertyBasedInterfaceMarshal()).create();
30
Guruprasad GV

私の知る限り、これは非コレクション型では機能しません。具体的には、具象型がシリアル化に使用され、インターフェイス型が非シリアル化に使用される状況です。つまり、インターフェイスを実装する単純なクラスがあり、具象クラスをシリアル化してから、インターフェイスを逆シリアル化するように指定すると、回復不可能な状況になります。

上記の例では、タイプアダプターがインターフェイスに対して登録されていますが、具象クラスを使用してシリアル化する場合は使用されません。つまり、CLASS_META_KEYデータは設定されません。

アダプターを階層アダプターとして指定すると(それにより、gsonに階層内のすべてのタイプに使用するように指示します)、シリアライザーはそれ自体を呼び出し続けるため、無限ループになります。

インターフェースの具体的な実装からシリアル化し、インターフェースとInstanceCreatorのみを使用して逆シリアル化する方法を知っている人はいますか?

デフォルトでは、gsonは具象インスタンスを作成するようですが、フィールドを設定しません。

問題はここに記録されます:

http://code.google.com/p/google-gson/issues/detail?id=411&q=interface

1
Jason Polites

Google GsonのTypeTokenクラスを使用する必要があります。もちろん、それを機能させるには、ジェネリッククラスTが必要です。

Type fooType = new TypeToken<Foo<Bar>>() {}.getType();

gson.toJson(foo、fooType);

gson.fromJson(json, fooType);
0
paul