2016-03-21 7 views
1

GSONを使ってMongoDB文書をPOJOに読み込む方法を探しています。あなたがデートやロングのようなものに遭遇するまではうまく動作します。GSONとTypeAdapterを使ってBSON(mongoDB)をPOJOに読む

私はGson用のカスタムアダプタを書いています。このアダプタは、長いBSONコードを変換します。私はこれが動作するかどうか確認するには、次のテストを定義している

public class BsonLongTypeAdapter extends TypeAdapter<Long> 
{ 
    @Override 
    public void write(JsonWriter out, Long value) throws IOException 
    { 
     out.beginObject() 
      .name("$numberLong") 
      .value(value.toString()) 
      .endObject(); 
    } 

    @Override 
    public Long read(JsonReader in) throws IOException 
    { 
     in.beginObject(); 
     assert "$numberLong".equals(in.nextName()); 
     Long value = in.nextLong(); 
     in.endObject(); 
     return value; 
    } 
} 

@Test 
public void canWriteCorrectJSON() { 
    Gson gson = new GsonBuilder().registerTypeAdapter(Long.class, new BsonLongTypeAdapter()).create(); 
    MyTestObject obj = new MyTestObject(1458569479431L); 
    String gsonString = gson.toJson(obj); 
    assertEquals("{\"timestamp\":{\"$numberLong\":\"1458569479431\"}}",gsonString); 
} 

@Test 
public void canReadFromJSON() { 
    Gson gson = new GsonBuilder().registerTypeAdapter(Long.class, new BsonLongTypeAdapter()).create(); 
    MyTestObject actualTaskObject = gson.fromJson("{\"timestamp\":{\"$numberLong\":\"1458569479431\"}}", MyTestObject.class); 
    MyTestObject taskObject = new MyTestObject(1458569479431L); 
    assertEquals(taskObject.getTimestamp(),actualTaskObject.getTimestamp()); 
} 

private static class MyTestObject 
{ 
    long timestamp; 

    public MyTestObject(long ts) 
    { 
     timestamp = ts; 
    } 
    public long getTimestamp() 
    { 
     return timestamp; 
    } 

    public void setTimestamp(long timestamp) 
    { 
     this.timestamp = timestamp; 
    } 
} 

最初の(書き込み)テストはうまく動作しますが、読み出しテストthisポストを読むと、私は自分のアダプタを作成しましたが失敗する:

com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected a long but was BEGIN_OBJECT at line 1 column 15 path $.timestamp 

私のアダプタからの読み取り機能が呼び出されないため。これは、私がMyTestObjectにマッピングしたいのであって、Longにはマッピングしたくないからだと推測しますが、longを含むすべてのクラスのアダプタを作成する必要はありません。

送信するすべてのBSONロングを変換するGSON用のアダプタを作成することはできますか?

答えて

0

CustomizedTypeAdapterFactoryを使用して解決しました。 See this question

は、基本的には最初のカスタマイズされたアダプタの書き込み:

public abstract class CustomizedTypeAdapterFactory<C> 
     implements TypeAdapterFactory 
{ 
    private final Class<C> customizedClass; 

    public CustomizedTypeAdapterFactory(Class<C> customizedClass) { 
     this.customizedClass = customizedClass; 
    } 

    @SuppressWarnings("unchecked") // we use a runtime check to guarantee that 'C' and 'T' are equal 
    public final <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { 
     return type.getRawType() == customizedClass 
       ? (TypeAdapter<T>) customizeMyClassAdapter(gson, (TypeToken<C>) type) 
       : null; 
    } 

    private TypeAdapter<C> customizeMyClassAdapter(Gson gson, TypeToken<C> type) { 
     final TypeAdapter<C> delegate = gson.getDelegateAdapter(this, type); 
     final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class); 
     return new TypeAdapter<C>() { 
      @Override public void write(JsonWriter out, C value) throws IOException 
      { 
       JsonElement tree = delegate.toJsonTree(value); 
       beforeWrite(value, tree); 
       elementAdapter.write(out, tree); 
      } 
      @Override public C read(JsonReader in) throws IOException { 
       JsonElement tree = elementAdapter.read(in); 
       afterRead(tree); 
       return delegate.fromJsonTree(tree); 
      } 
     }; 
    } 

    /** 
    * Override this to muck with {@code toSerialize} before it is written to 
    * the outgoing JSON stream. 
    */ 
    protected void beforeWrite(C source, JsonElement toSerialize) { 
    } 

    /** 
    * Override this to muck with {@code deserialized} before it parsed into 
    * the application type. 
    */ 
    protected void afterRead(JsonElement deserialized) { 
    } 
} 

をそして考慮に入れる必要があるすべてのクラスのサブクラスを作成します。長い(この場合は)クラスを含むすべてのクラスに対して1つずつ作成する必要があります。しかし、あなたは長い値(および他のBSON特定の値)以外のものをシリアル化する必要が

public class MyTestObjectTypeAdapterFactory extends CustomizedTypeAdapterFactory<MyTestObject> 
{ 
    public MyTestObjectTypeAdapterFactory() 
    { 
     super(MyTestObject.class); 
    } 

    @Override 
    protected void beforeWrite(MyTestObject source, JsonElement toSerialize) 
    { 
     //you could convert back the other way here, I let mongo's document parser take care of that. 
    } 

    @Override 
    protected void afterRead(JsonElement deserialized) 
    { 
     JsonObject timestamp = deserialized.getAsJsonObject().get("timestamp").getAsJsonObject(); 
     deserialized.getAsJsonObject().remove("timestamp"); 
     deserialized.getAsJsonObject().add("timestamp",timestamp.get("$numberLong")); 
    } 
} 

し、その後でGsonを生成しません:あなたはモンゴにこれを接続しているコードが欠落している

Gson gson = new GsonBuilder().registerTypeAdapterFactory(new MyTestObjectTypeAdapterFactory()).create(); 
+0

、Mongoはあなたのアダプターを使用するのはなぜですか?BSONは使用できません。 – Dudi

+0

それはあなた次第です。このコードはbson文字列(mongodb出力のような)をpojoとbackに変換するために使うことができます。 –

+0

gsonオブジェクトに登録されたAFAIK TypeAdapterは、そのgsonを呼び出すときに使用されます。しかし、私がそれを引き出すときに私がモンゴの文書を持っていれば、それは自動的にそれをオブジェクトにしようとします。私はMongoCollection を返すfind()を使用していますので、TypeAdapterを使用してTEEに自動デシリアライズします – Dudi

関連する問題