FuelとMoshiの連携

# 検証環境
この記事の内容は、以下の環境で検証しました。

* Intellij IDEA ULTIMATE 2017.2
* Kotlin 1.1.61
* Fuel 1.12.0
* Moshi 1.5.0
* Java 1.8
* Gradle Projectで作成

# 目標
Fuelには、JSONライブラリーと連携する機能が備わっています。
本記事は、『FuelとMoshiの連携方法を理解する』を目標とします。

【連携可能なライブラリー】
* GSON
* Moshi
* Jackson

https://github.com/square/moshi

# 前提
下記の記事を理解していることを前提として、本記事を記述しています。

https://casareal-ls.qiita.com/naoi/items/bd97274e012501f884aa

https://casareal-ls.qiita.com/naoi/items/64413e2777210526aa87


# 事前準備
本記事とは直接関係ないですが、サンプルで使用しているクラスを事前に説明しておきます。

## Userデータクラス
JSONからオブジェクトやオブジェクトからJSONにパースする際に使用するクラスです。

```kotlin
data class User(val name:String,val detail:UserDetail)
```
## UserDeatailデータクラス
Userクラスのプロパティとして保持するクラスです。

```kotlin
data class UserDetail(val age:Int, val job:String)
```

## 返ってくるJSON

今回サンプル用にローカルに用意したサーバで返すJSONは以下の通りです。
### 単一オブジェクトのJSON
URL
http\://localhost:8080/userInfo
JSON

```json
{"detail":{"age":25,"job":"会社員"},"name":"山田太郎"}
```
### 配列形式のJSON
URL
http\://localhost:8080/multiUserInfo

```json
[{"detail":{"age":66,"job":"偉人"},"name":"福沢諭吉"},{"detail":{"age":24,"job":"作家"},"name":"樋口一葉"},{"detail":{"age":51,"job":"医師"},"name":"野口英世"}]
```


# 実装例

## 単一オブジェクトのJSON
### コード例
レスポンスのJSONをデータクラスに変換するコードは、以下の通りです。

```kotlin
    "http://localhost:8080/userInfo".httpGet().responseObject<User> { req, res, result ->
        val(user,err) = result
        println("user:${user}");
    }
```

### 解説
レスポンスで受け取ったデータを指定したデータクラスの型に変換するには、『responseObject関数』を使用します。
関数のジェネリクスにデータクラスのを指定するだけで変換します。

```kotlin
responseObject<User>
```
resultにUserデータクラスに変換したオブジェクトが格納されています。
今回は分解宣言で取得しています。userという変数にUserデータクラスのオブジェクトが格納されます。

```kotlin
val(user,err) = result
```

### 実行結果

```text
user:User(name=山田太郎, detail=UserDetail(age=25, job=会社員))
```

## 配列形式のJSON
### コード例
配列形式のJSONをUserデータクラスを格納したリストに変換するコードは、以下の通りです。

JSONをList<User>型に変換する処理を記述したクラス

```kotlin
package jp.co.casareal.fuelmoshi

import com.github.kittinunf.fuel.core.ResponseDeserializable
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi
import com.squareup.moshi.Types

class UserListDeserializer : ResponseDeserializable<List<User>> {

    override fun deserialize(content: String): List<User>? {
        val moshi = Moshi.Builder().build()
        val type = Types.newParameterizedType(List::class.java, User::class.java)
        val listAdapter: JsonAdapter<List<User>> = moshi.adapter(type)

        return listAdapter.fromJson(content)
    }
}
```

HTTP通信を行い、レスポンスをUserListDeserializerでList<User>型に変換する処理

```kotlin
    "http://localhost:8080/multiUserInfo".httpGet().responseObject(UserListDeserializer()) { req, res, result ->
        val(list,err) = result

        list?.forEach {
            println(it)
        }
    }
```

### 解説
配列のJSONを受け取る場合は、responseObject関数のジェネリクスで指定しても、List型に正しく変換されません。
変換するには、ResponseDeserializableを継承し、deserializeメソッドとオーバライドしたクラスを用意する必要があります。

```kotlin
class UserListDeserializer : ResponseDeserializable<List<User>> {
    override fun deserialize(content: String): List<User>? {
       　　・・・省略・・・
    }
}
```

deserializeメソッド内でJSONからオブジェクトに変換する処理を行います。
処理内容詳細は、下記の記事に記述しています。
https://casareal-ls.qiita.com/naoi/items/64413e2777210526aa87

```kotlin
val moshi = Moshi.Builder().build()
val type = Types.newParameterizedType(List::class.java, User::class.java)
val listAdapter: JsonAdapter<List<User>> = moshi.adapter(type)

return listAdapter.fromJson(content)
```

deserializeメソッドはオーバロードされており、引数の型を以下から選べます。
* public fun deserialize(bytes: ByteArray): T?
* public fun deserialize(inputStream: InputStream): T?
* public fun deserialize(reader: Reader): T?
* public fun deserialize(content: String): T?

HTTP通信は単一オブジェクトのJSONと同じようにresponseObject関数を利用します。
responseObject関数の引数にUserListDeserializerクラスのインスタンスを渡すことによって
作成したUserListDeserializerクラスのdeserializeメソッドが動作し、変換処理が行われます。

```kotlin
responseObject(UserListDeserializer()) 
```

以降は単一オブジェクトのJSONと同じ為、説明は省略します。

### 実行結果

```text
User(name=福沢諭吉, detail=UserDetail(age=66, job=偉人))
User(name=樋口一葉, detail=UserDetail(age=24, job=作家))
User(name=野口英世, detail=UserDetail(age=51, job=医師))
```