ScalaでAndroid開発する場合のシリアライズ／デシリアライズ

#経緯

アプリの状態をプリファレンス（DBでも何でも良いですが）に保存したかったのです。
通常のJavaによるAndroid開発においては、単一クラス配下に状態をまとめて、文字列にシリアライズして保存、というのが常套手段のようです。
おそらくそれが一番楽だろうと思われるので踏襲することにしました。

#実現方法

Javaの場合はGsonを使うのがメジャーな手法のようです。
一方Scalaの場合ですが、ScalaでのAndroidアプリ開発がマイナーである以上、当然メジャーな手法など存在しません。
シリアライズ／デシリアライズを行うことができるライブラリはいくつか存在し、Json系ライブラリでシリアライズ／デシリアライズ機能を含むものと、最初からシリアライズ／デシリアライズを目的としたPickle系ライブラリの2種類に大きく分けられます。
前者で有名なのがJson4sやspray-jsonとかPlay Frameworkのjsonパッケージ、後者で有名なのはPicklingでしょうか。

#検討

いくつかのライブラリを検討してみました。

##Json4s

javax.inject.NamedクラスがAndroid APIに存在しないのでビルド通らず。

##spray-json

シリアライズ対象のクラスごとにjsonFormatNを定義しなければいけません。
また、代数的データ型（sealed traitとcase objectで実装された列挙型）については個別に変換関数を用意してやらなければいけないため、今回は自前実装とほぼ工数が変わらないことが判明し、パス。

##それ以外のJson系ライブラリ

そもそもAndroidで使用する場合、ライブラリは極力小さいことが望ましいということに気付き、パスしました。

##Pickling

ProGuard関連で引っかかります。軽く調べた感じだと対処は可能そうなのですがハマりそうなので一旦パス。

##BooPickle

バイナリへのシリアライズ／デシリアライズしかサポートしていないようなのでパス。

##uPickle

最終的にこれを採用しました。詳細は[こちら](http://www.lihaoyi.com/upickle-pprint/upickle/)。

#実装

以下のコードでシリアライズ／デシリアライズを試してみたところ、動作しました。


```scala:MainActivity.scala
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.widget.TextView

import upickle.default._

sealed trait EnumLike
object EnumLike {
  case object A extends EnumLike
  case object B extends EnumLike
  case object C extends EnumLike
}

sealed abstract class EnumLikeHasField(val number: Int, val text: String)
object EnumLikeHasField {
  case object A extends EnumLikeHasField(1, "A")
  case object B extends EnumLikeHasField(2, "B")
  case object C extends EnumLikeHasField(3, "C")
}

case class InnerData(number: Int, text: String, enumLike: EnumLike, enumLikeHasField: EnumLikeHasField)

case class Data(number: Int, text: String, enumLike: EnumLike, enumLikeHasField: EnumLikeHasField, inner: InnerData)

class MainActivity extends AppCompatActivity {
  override def onCreate(savedInstanceState: Bundle): Unit = {
    super.onCreate(savedInstanceState)

    val before: Data = Data(1, "test", EnumLike.B, EnumLikeHasField.B, InnerData(3, "inner", EnumLike.C, EnumLikeHasField.C)) 
    val serialized: String = write(before)
    val after: Data = read[Data](serialized)

    val text = new TextView(this);
    text.setText(before == after);
    setContentView(text)
  }
}
```

#しかし問題が

シリアライズ／デシリアライズの対象クラスとwrite/readが別ファイルだと、コンパイル時に以下のエラーが発生します。

```
[error] C:\Temp\pickle\uPickle\src\main\scala\com\gmail\lsetzl\test\MainActivity.scala:15: The referenced trait [[EnumLike]] does not have any sub-classes. This may happen due to a limitation of scalac (SI-7046). To work around this, try manually specifying the sealed trait picklers as described in http://lihaoyi.github.com/upickle-pprint/upickle/#ManualSealedTraitPicklers
[error]     val serialized: String = write(before)
[error]                              ^
[error] C:\Temp\pickle\uPickle\src\main\scala\com\gmail\lsetzl\test\MainActivity.scala:16: The referenced trait [[EnumLike]] does not have any sub-classes. This may happen due to a limitation of scalac (SI-7046). To work around this, try manually specifying the sealed trait picklers as described in http://lihaoyi.github.com/upickle-pprint/upickle/#ManualSealedTraitPicklers
[error]     val after: Data = read[Data](serialized)
[error]                       ^
[error] two errors found
```

親切なメッセージですが、示されたURLに記載された方法を試しても状況は変化するものの解決しません。
調べてみたところ、SI-7046はScala2.12.1では解決済（partial fixとあるので、部分的解決のようですが・・・）。
しかしご存知の通り、2.12からはJava7が切り捨てられているのでAndroidアプリ開発時は使用不可。どーせいっちゅーねん。

#解決（？）

結局、件のバグフィクスがバックポートされている2.11.9のNightly buildを頼ることにしました。Nightly buildの使用方法については[こちら](http://qiita.com/Setz/items/90ab3f68a8a6ba6a2cd3)を参照してください。
趣味で開発しているAndroidアプリかつ早期のリリースが見込めない状況のため、開発中に2.11.9のリリースがあるだろうという見通しです。
試してみたところ、上記のコードのままでシリアライズ／デシリアライズを行うことができました。