moshi-adaptersの紹介

# はじめに

Moshi-adaptersはMoshiのアドオンライブラリです。
Moshi公式からリリースされています。
まずはMoshiって何？って方のために、簡単にMoshiの紹介から。

# Moshi

MoshiはJavaでJsonを扱うためのライブラリです。
またMoshiはKotlinと相性が良く、Kotlinと一緒に利用されていることが多いです。

Json変換は標準的なデータ型とjsonとの相互変換をサポートしています。

* int, float, charなどのプリミティブ型および、それらのオブジェクト型(Integer, Float, Character)
* Arrays, List, Mapなど
* String
* Enum

# Moshi-codegen

Moshi-codegenはKotlinでMoshiを利用するときに便利なライブラリです。
Moshiではサポートしていない型(自作Classなど)を扱うときにはAdapterを作成する必要があります。
ですが、Moshi-codegenはそれをannotation processorで生成してくれます。
Moshi-codegenはMoshi公式からリリースされています。

以下のように変換したいクラスにアノテーションをつけることでAdapterが自動生成されます。
これはKotlinのdata classも対象にできます。

```Kotlin
@JsonClass(generateAdapter = true)
class Item(val name: String)

@JsonClass(generateAdapter = true)
data class Cart(val items: List<Item>)

val adapter = Moshi.Builder().build().adapter(Cart::class.java)

val cart = Cart(items = listOf(Item("りんご"), Item("電球")))
val jsonString = adapter.toJson(cart)
println(jsonString) // {"items":[{"name":"りんご"},{"name":"電球"}]}
```

# Moshi-adapters

ようやく本題のMoshi-adaptersです。
これはいくつかのAdapterを追加するライブラリです。
version 1.8.0では、時刻に関するAdapterとEnumに関するAdapter、継承したクラスのAdapterがあります。

## EnumJsonAdapter

MoshiはもともとEnumとjsonの相互変換に対応しています。

```Kotlin
enum class Type {
  A,B,UNKNOWN
}
val adapter = Moshi.Builder().build().adapter(Type::class.java)

val type = Type.A
val jsonString = adapter.toJson(type)
println(jsonString) // "A"
```

しかし、知らないStringが来たときはEnumにマップできず例外が投げられます。

```Kotlin
val fromJson = try {
  adapter.fromJson("\"C\"")
} catch (e: Exception) {
  null
}
println("fromJson:$fromJson") // fromJson:null
```

CというEnumは定義されていないので例外が発生して`fromJson`は`null`になってしまいます。
新しいEnum値を追加したとき、古いコードではUNKNOWNにするなどして逃げたい、ということがありますよね。
そういう時に使うのがEnumJsonAdapterです。
EnumJsonAdapterはEnumで知らない値が来たときのデフォルト値を設定するためのAdapterです。
使い方を見ましょう。

```Kotlin
val adapter = Moshi.Builder()
        .add(Type::class.java, 
                EnumJsonAdapter.create(Type::class.java).withUnknownFallback(Type.UNKNOWN))
        .build().adapter(Type::class.java)

val fromJson = try {
  adapter.fromJson("\"C\"")
} catch (e: Exception) {
  null
}
println("fromJson:$fromJson") // fromJson:UNKNOWN
```

このように、未知の値`C`がやってきたので`withUnknownFallback`で指定した`UNKNOWN`に変換されました。

## PolymorphicJsonAdapterFactory

あるスーパークラスがあって、それを継承したクラスのjson変換をしたい場合に利用します。
これを使うと、継承したクラスをjsonへシリアライズすることも、jsonからデシリアライズすることもできるようになります。
例を見ましょう。

```Kotlin
sealed class SealedItem(val name: String) {
    @JsonClass(generateAdapter = true)
    class Food(name:String) : SealedItem(name)

    @JsonClass(generateAdapter = true)
    class Appliances(name:String) : SealedItem(name)
}

@JsonClass(generateAdapter = true)
data class SealedCart(val items: List<SealedItem>)
```

このように、SealedItemというクラスがあって、それを継承したFoodとAppliancesがあったとします。
これをjsonに変換したり、jsonから戻したりするときに利用するのがPolymorphicJsonAdapterFactoryです。
使い方を見ましょう。

```Kotlin
val cart = SealedCart(items = listOf(SealedItem.Food("りんご"),
       SealedItem.Appliances("電球")))

val adapter = Moshi.Builder()
        .add(PolymorphicJsonAdapterFactory.of(SealedItem::class.java, "type")
                .withSubtype(SealedItem.Food::class.java, "food")
                .withSubtype(SealedItem.Appliances::class.java, "appliances"))
        .build().adapter(SealedCart::class.java)

val jsonString = adapter.toJson(cart)
val fromJson = adapter.fromJson(jsonString)

println(jsonString)
// {"items":[{"type":"food","name":"りんご"},{"type":"appliances","name":"電球"}]}
```

このように、json変換したときに`of`で指定したプロパティが追加され、そのプロパティに`withSubtype`で型毎に指定した値がセットされるようになります。
このようにjson変換してくれるAdapterを生成できるのがPolymorphicJsonAdapterFactoryです。

# まとめ

Moshiを利用しているなら、ぜひMoshi-adaptersも利用しましょう！