Help us understand the problem. What is going on with this article?

moshi-adaptersの紹介

More than 1 year has passed since last update.

はじめに

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も対象にできます。

@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の相互変換に対応しています。

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にマップできず例外が投げられます。

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

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

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からデシリアライズすることもできるようになります。
例を見ましょう。

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です。
使い方を見ましょう。

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も利用しましょう!

soranakk
dena_coltd
    Delight and Impact the World
https://dena.com/jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away