Retrofit2を使ってGitHub APIを叩くアプリケーションを作成しているのですが,  
例えば[Event API](https://developer.github.com/v3/activity/events/#events)の場合,
`type`フィールドの値によって後続の `payload`の中身が決定されます.

`type`が`"PushEvent"`であれば...

```

    "payload": {
        "push_id": xxxx,
        "size": 1,
        "distinct_size": 1,
        "ref": "refs/heads/master",
        "head": "aaaa",
        "before": "bbbb",
        "commits": [
            {
              "sha": "aaaa",
              "author": {
                  "email": "hoge",
                  "name": "hoge"
              },
              "message": "xxx",
              "distinct": true,
              "url": "xxx"
            }
        ]
    },
    
```

`type`が`"PullRequestEvent"`であれば...

```

    "payload": {
        "action": "opened",
        "number": 1,
        "pull_request": {
            "url": "xxx",
            "id": 0000,
            "html_url": "xxx",
            "diff_url": "xxx",
            "patch_url": "xxx",
            "issue_url": "xxx",
            "number": 0,
            "state": "open",
            "locked": false,
            "title": "xxx",
            ...
    
```

といった具合です.

上記のケースにおいて,
レスポンスの中身を見て`AbstractEvent`の具象クラス(例えば`PushEvent`や`PullRequestEvent`)を生成する方法も考えられますが,  
これをRetrofit2で実現する場合のベストプラクティスを模索しています.

例えば下記の`GET API`を定義しておき,

```

    @GET("/users/{username}/events/public")
    Observable<List<AbstractEvent>> listEvent(@Path("username") String username);
    
```

`Custom
Converter`の機能を使って`PushEvent`オブジェクトや`PullRequestEvent`オブジェクトを生成する方法が妥当なのでしょうか？  
こういったケースでのRetrofit2の作法をご教授いただけると幸いです.

* * *

**追記 : 2016.04.08**

(一部自己解決しました)  
現状、上記のケースでは下記の実装方法がしっくりきました.

`Events`の`Type`と`Json`変換後の`VO`を持つ`Enum`を定義.

```

    public enum EventType {
        @SerializedName("PullRequestEvent")
        PullRequestEvent(PullRequestEvent.class),
        @SerializedName("PushEvent")
        PushEvent(PushEvent.class),
    
        private Type type;
    
        EventType(Type type) {
            this.type = type;
        }
    
        public Type getType() {
            return type;
        }
    }
    
```

自前の`JsonDeserializer`を用意.  
`payload`の中身を引数に先ほどの`Enum`から`VO`を導出しパースする.

```

    public static class EventDeserializer implements JsonDeserializer<Event> {
    
        @Override
        public Event deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext context)
                throws JsonParseException {
            Event message = new Gson().fromJson(jsonElement, Event.class);
            if (message == null) {
                return null;
            }
    
            EventType eventType = message.getType();
            JsonElement data = jsonElement.getAsJsonObject().get("payload");
            if (eventType == null || data == null) {
                message.payload = null;
                return message;
            }
    
            message.payload = new Gson().fromJson(data, eventType.getType());
            return message;
        }
    }
    
```

`Retrofit`には`TypeAdapter`を設定.

```

    protected GitHubApi createGitHubApi() {
        Gson gson = new GsonBuilder()
                .registerTypeAdapter(Event.class, new Event.EventDeserializer())
                .create();
    
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(GitHubApi.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build();
        return retrofit.create(GitHubApi.class);
    }
    
```

[GitHub.java](https://github.com/YukiMatsumura/HusaHusa/blob/master/app/src/main/java/yuki/m/android/husahusa/data/GitHub.java)
CC BY-SA 3.0
2016-04-04T14:45:06.520
23744
2016-04-07T16:00:34.710
2016-04-07T16:00:34.710
15039
15039
question
android java retrofit
Retrofit2で動的なJsonフォーマットに対応する方法