18
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

DeNAAdvent Calendar 2018

Day 11

moshi-adaptersの紹介

Last updated at Posted at 2018-12-10

はじめに

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

18
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?