2017-01-18 10 views
1

Retrofit2とのRESTインターフェイス用のスタブを書くときによく見られることは、実際のパラメータを単一のエントリを持つマップでラップする必要があることです(または、単一のフィールドを持つクラスで構成されるラッパークラス)。JSONラッパーの「単一エントリ」マップを回避することは可能ですか?

一般的なJSONペイロードは、{"idontcareaboutthis": { // Data I actually want...のようになります。この比較的無駄な外殻を剥がす方法はありますか?私のすべてのRESTメソッドがMapの戻り値型を持っているのは奇妙なことです。

+0

コンバーターを使用します。 1. http://square.github.io/retrofit/#restadapter-configuration 2. 'Retrofit.Builder.addConverterFactory(Converter.Factory factory)' 3.(Gsonを使用している場合)GsonConverterFactoryに似たファイルを作成します。カスタム論理を追加するjava。 – Sangharsh

答えて

1

マップは必要ありません。独自のJSONデシリアライザを作成できます。あなたがキー単一のルートオブジェクトを必要としない以下のJSONを持っている、のは、言ってみましょう:

{ 
    "idontcareaboutthis": { 
     "foo": 1, 
     "bar": 2 
    } 
} 

次にJSONのデシリアライザは、次のようになります。デシリアライザは、上記の抽出方法

final class ResponseJsonDeserializer<T> 
     implements JsonDeserializer<T> { 

    private final Gson backingGson; 

    private ResponseJsonDeserializer(final Gson backingGson) { 
     this.backingGson = backingGson; 
    } 

    static <T> JsonDeserializer<T> getResponseJsonDeserializer(final Gson backingGson) { 
     return new ResponseJsonDeserializer<>(backingGson); 
    } 

    @Override 
    public T deserialize(final JsonElement json, final Type type, final JsonDeserializationContext context) 
      throws JsonParseException { 
     final JsonObject root = json.getAsJsonObject(); 
     final Set<Entry<String, JsonElement>> entries = root.entrySet(); 
     final int propertyCount = entries.size(); 
     if (propertyCount != 1) { 
      throw new JsonParseException("Expected a single property root object, but got an object with " + propertyCount + " properties"); 
     } 
     final Entry<String, JsonElement> inner = entries.iterator().next(); 
     // Can't use context.deserialize here due to recursion 
     return backingGson.fromJson(inner.getValue(), type); 
    } 

} 

注意他のGsonインスタンスにデシリアライズプロセスを委譲する方法について説明します。これで、idontcareaboutthisプロパティを認識しているGsonインスタンスを作成する必要があります。

private static final Gson registryBackingGson = new GsonBuilder() 
      // whatever necessary here 
      .create(); 

    private static final Gson registryGson = new GsonBuilder() 
      .registerTypeAdapter(FooBarResponse.class, getResponseJsonDeserializer(registryBackingGson)) 
      // add another response classes here like the above, but do not register other types - they must be registered in registryBackingGson 
      .create(); 

registryGsonすべての応答クラスを列挙したり、特定のタイプの階層を登録するのいずれかが必要です。最初のケースがそれほど便利ではなく、レスポンスクラスのソースコードを変更できる場合は、タイプマーカー全体を登録するための特別なマーカーインターフェイスを追加することができます。言って、このような何か:

private static final Gson registryBackingGson = new GsonBuilder() 
      // whatever necessary here 
      .create(); 

    private static final Gson registryGson = new GsonBuilder() 
      .registerTypeHierarchyAdapter(IResponse.class, getResponseJsonDeserializer(registryBackingGson)) 
      // no need to add another "response" classes here - they just must implement the marker interface 
      .create(); 

データ転送オブジェクト:

final class FooBarResponse { 

    // The `final` modifier is a reasonable habit for incoming DTO classes, but primitive constants are inlined by the compiler. 
    // Suppressing the inlining can be done be a simple workaround to make javac think that it's not a real constant. 
    // However, it's a matter of your code style, and this is just an example. 
    private final int foo = constOf(0); 
    private final int bar = constOf(0); 

    int getFoo() { 
     return foo; 
    } 

    int getBar() { 
     return bar; 
    } 

    // We're cheating... 
    private static int constOf(final int i) { 
     return i; 
    } 

} 

そして、あなたはマーカーインターフェイスを好むと

interface IResponse { 
} 

final class FooBarResponse 
     implements IResponse { 
... 

を全タイプの階層を登録するかのそして、どのようにそれが動作します:

final FooBarResponse fooBar = registryGson.fromJson(JSON, FooBarResponse.class) 
out.println(fooBar.getFoo()); // 1 
out.println(fooBar.getBar()); // 2 

レトロフィットアダプタ:

final Retrofit retrofit = new Retrofit.Builder() 
     // ... 
     .addConverterFactory(GsonConverterFactory.create(registryGson)) 
     .build(); 

このように、あなたのレトロフィット・ベースのインタフェースメソッドがFooBarの/ etcクラスのインスタンスではなく、マップを返すことができます。

関連する問題