2013-03-26 2 views
3

GraphAdapterBuilder은 GSON 라이브러리에 추가되어 순환 참조가있는 객체를 직렬화하려고합니다. 그것은 클래스를 위해 잘 작동하지만 인터페이스를 deserialize하려고 할 때 실패합니다.GSON GraphAdapterBuilder가 인터페이스에서 실패 함

인터페이스를 역 직렬화하려면 (GSON은 기본적으로 수행하지 않음) PropertyBasedInterfaceMarshal 또는 InterfaceAdapter을 사용하고 있습니다. 이들은 인터페이스에 대한 사용자 정의 유형 어댑터로 등록됩니다.

위의 ether를 사용하면 GraphAdapterBuilder에 의해 생성 된 "0x4"와 같은 그래프 ID 만 전달되므로 인터페이스를 deserialize 할 수 없습니다. 이것은 디시리얼라이저에서 JsonElement로 전달됩니다. 분명히 디시리얼라이저 내부에서이 ID로 수행 할 수있는 것은 없습니다.

역 직렬화를 시도하는 대신 GraphAdapterBuilder에서이를 가져야하지 않습니까? 이 문제를 해결할 수 없었습니다. 이것이 GraphAdapterBuilder의 버그입니까? 아니면이 문제를 해결할 수있는 방법이 있습니까?

+1

당신이 가지고있는 클래스들과 그들 사이의 의존성을 제공 할 수 있습니까? 사소한 속성없이 클래스 만. –

답변

1

좋습니다, 해결책을위한 (작동중인) 스텁입니다. 이탈리아에서 너무 늦어서 더 좋지 않습니다.

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); 
    } 
} 

및 일부 샘플 클래스와 같은 시험을 한 쌍 (확인 :

는이

package com.google.gson.graph; 

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

이 같은 TypeAdapterFactory 같은 위임 기능이 필요합니다 테스트가 통과 함)

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; 
    } 
} 

이것은 빠른 & 더러운 해킹 이었지만, 그것은 charme처럼 작동합니다.

GsonBuilder를 초기화하는 동안 작업 순서에주의를 기울여야합니다. 이 팩토리를 등록한 후에 만 ​​먼저 GraphAdapterBuilder을 초기화하고 등록해야합니다.

자바 전문가가 아니기 때문에 조금 까다로운 경우 재밌습니다.

+0

기본 인터페이스 대신 기본 추상 클래스를 사용해 보았는데 완벽하게 작동했습니다. 고맙습니다. –