4

GSONライブラリの追加機能であるGraphAdapterBuilderを使用して、循環参照を使用してオブジェクトをシリアル化しようとしています。クラスには最適ですが、インターフェイスを逆シリアル化しようとすると失敗します。

インターフェイスを逆シリアル化するために(GSONはデフォルトでは実行しません)、PropertyBasedInterfaceMarshalまたはInterfaceAdapterを使用しています。これらは、インターフェイスのカスタムタイプアダプタとして登録されています。

上記のetherを使用すると、GraphAdapterBuilderによって生成された「0x4」のようなグラフIDのみが渡されるため、両方ともインターフェイスの逆シリアル化に失敗します。これは、デシリアライザーでJsonElementとして渡されます。明らかに、デシリアライザー内からこのIDで実行できることは何もありません。

これらは、逆シリアル化しようとするのではなく、GraphAdapterBuilderによってキャッチされるべきではありませんか?私はこれを回避することができませんでした、これはGraphAdapterBuilderのバグですか、それともこれを回避する方法はありますか?

4

1 に答える 1

2

わかりました、これはソリューションの(機能する)スタブです。イタリアでは手遅れで、それをより良くすることはできません。

このようなデリゲート機能が必要です

package com.google.gson.graph;

/**
 * @author Giacomo Tesio
 */
public interface GenericFunction<Domain, Codomain> {
    Codomain map(Domain domain);
}

次のようなTypeAdapterFactory:

package com.google.gson.graph;

import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;


/**
 * @author Giacomo Tesio
 */
public class InterfaceAdapterFactory  implements TypeAdapterFactory {

    final Map<String, GenericFunction<Gson, TypeAdapter<?>>> adapters;
    private final Class<?> commonInterface;
    public InterfaceAdapterFactory(Class<?> commonInterface, Class<?>[] concreteClasses)
    {
        this.commonInterface = commonInterface;
        this.adapters = new HashMap<String, GenericFunction<Gson, TypeAdapter<?>>>();
        final TypeAdapterFactory me = this;
        for(int i = 0; i < concreteClasses.length; ++i)
        {
            final Class<?> clazz = concreteClasses[i];
            this.adapters.put(clazz.getName(), new GenericFunction<Gson, TypeAdapter<?>>(){
                public TypeAdapter<?> map(Gson gson) {
                     TypeToken<?> type = TypeToken.get(clazz);
                     return gson.getDelegateAdapter(me, type);
                }
            });
        }
    }
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
        if(!this.commonInterface.isAssignableFrom(type.getRawType())
           && !this.commonInterface.equals(type.getRawType()))
        {
            return delegate;
        }
        final TypeToken<T> typeToken = type;
        final Gson globalGson = gson;
        return new TypeAdapter<T>() {
           public void write(JsonWriter out, T value) throws IOException {
             out.beginObject();
             out.name("@t");
             out.value(value.getClass().getName());
             out.name("@v");
             delegate.write(out, value);
             out.endObject();
           }
           @SuppressWarnings({"unchecked"})
           public T read(JsonReader in) throws IOException {
               JsonToken peekToken = in.peek();
               if(peekToken == JsonToken.NULL) {
                   in.nextNull();
                   return null;
               }

               in.beginObject();
               String dummy = in.nextName();
               String typeName = in.nextString();
               dummy = in.nextName();
               TypeAdapter<?> specificDelegate = adapters.get(typeName).map(globalGson);
               T result = (T)specificDelegate.read(in);
               in.endObject();
               return result;
           }
        };
    }

}

これらのようなテストのペア

public final class InterfaceAdapterFactoryTest extends TestCase {

    public void testInterfaceSerialization1(){
        SampleInterface first = new SampleImplementation1(10);
        SampleInterfaceContainer toSerialize = new SampleInterfaceContainer("container", first);

        GsonBuilder gsonBuilder = new GsonBuilder();

        new GraphAdapterBuilder()
            .addType(SampleInterfaceContainer.class)
            .addType(SampleImplementation1.class)
            .addType(SampleImplementation2.class)
            .registerOn(gsonBuilder);
        gsonBuilder.registerTypeAdapterFactory(new InterfaceAdapterFactory(
                SampleInterface.class, new Class<?>[] { SampleImplementation1.class, SampleImplementation2.class }
                ));
        Gson gson = gsonBuilder.create();

        String json = gson.toJson(toSerialize);
        System.out.println(json);
        SampleInterfaceContainer deserialized = gson.fromJson(json, SampleInterfaceContainer.class);

        assertNotNull(deserialized);
        assertEquals(toSerialize.getName(), deserialized.getName());
        assertEquals(toSerialize.getContent().getNumber(), deserialized.getContent().getNumber());
    }

    public void testInterfaceSerialization2(){
        SampleImplementation2 first = new SampleImplementation2(5, "test");
        SampleInterfaceContainer toSerialize = new SampleInterfaceContainer("container", first);
        first.Container = toSerialize;

        GsonBuilder gsonBuilder = new GsonBuilder();

        new GraphAdapterBuilder()
            .addType(SampleInterfaceContainer.class)
            .addType(SampleImplementation1.class)
            .addType(SampleImplementation2.class)
            .registerOn(gsonBuilder);
        gsonBuilder.registerTypeAdapterFactory(new InterfaceAdapterFactory(
                SampleInterface.class, new Class<?>[] { SampleImplementation1.class, SampleImplementation2.class }
                ));
        Gson gson = gsonBuilder.create();

        String json = gson.toJson(toSerialize);
        System.out.println(json);
        SampleInterfaceContainer deserialized = gson.fromJson(json, SampleInterfaceContainer.class);

        assertNotNull(deserialized);
        assertEquals(toSerialize.getName(), deserialized.getName());
        assertEquals(5, deserialized.getContent().getNumber());
        assertEquals("test", ((SampleImplementation2)deserialized.getContent()).getName());
        assertSame(deserialized, ((SampleImplementation2)deserialized.getContent()).Container);
    }
}

およびいくつかのサンプルクラス(テストに合格したことを確認するため)

public class SampleInterfaceContainer {
    private SampleInterface content;
    private String name;
    public SampleInterfaceContainer(String name, SampleInterface content)
    {
        this.name = name;
        this.content = content;
    }

    public String getName()
    {
        return this.name;
    }

    public SampleInterface getContent()
    {
        return this.content;
    }
}
public interface SampleInterface {
    int getNumber();
}
public class SampleImplementation1 implements SampleInterface{
    private int number;
    public SampleImplementation1()
    {
        this.number = 0;
    }
    public SampleImplementation1(int number)
    {
        this.number = number;
    }
    public int getNumber()
    {
        return this.number;
    }
}

public class SampleImplementation2 implements SampleInterface{
    private  int number;
    private String name;
    public SampleInterfaceContainer Container;
    public SampleImplementation2()
    {
        this.number = 0;
        this.name = "";
    }
    public SampleImplementation2(int number, String name)
    {
        this.number = number;
        this.name = name;
    }
    public int getNumber()
    {
        return this.number;
    }
    public String getName()
    {
        return this.name;
    }
}

これは手っ取り早いハックですが、魅力のように機能します。

GsonBuilderの初期化中は、操作の順序に注意を払う必要があります。このファクトリを登録した後でのみ、最初にGraphAdapterBuilderを初期化して登録する必要があります。

それは面白かったです(私はJavaの専門家ではないので少し注意が必要です)。

于 2013-04-05T01:01:08.283 に答える