ScalaのJSONライブラリにはこちらのスレッドによると、ざっと下の選択肢がある。多い...
- parsing.json.JSON - 標準ライブラリ
- spray-json
- Jerkson - JacksonのScala用ラッパ
- sjson
- lift-json
- json4s - lift-json系
- Argonaut - Scalaz系
- play-json
- Jackson - Javaライブラリ
今回は、とっつきやすそうで、pull型(Streaming)パースをサポートしていて、Java界で実績がありそうなJacksonを試すことにした。環境はsbt-androidで、とりあえずJSONをScalaのオブジェクトとして取得(データバインド)してみることにした。
1. 依存関係の追加
build.sbtに jackson-core, jackson-databind を追加。
libraryDependencies += "com.fasterxml.jackson.core" % "jackson-core" % "2.3.0"
libraryDependencies += "com.fasterxml.jackson.core" % "jackson-databind" % "2.3.0"
2. モデルクラスの定義
引数無しコンストラクタと、プロパティごとにset*系のメソッドを定義する。
package com.example.app1.model
class MyModel1(
var prop1: String,
var prop2: Int
) {
def this() = { this(null, -1) }
def setProp1(a: String) { this.prop1 = a }
def setProp2(a: Int) { this.prop2 = a }
}
JSONの生成もする場合はget*系も定義をする(略)。
3. JSONの読み込み例
package com.example.app1
import com.fasterxml.jackson.databind.ObjectMapper
import com.example.app1.model.MyModel1
class Class1 {
def method1 = {
...
val json = """{"prop1": "value1", "prop2": 1234}"""
val model1 = (new ObjectMapper).readValue(json, classOf[MyModel1])
model1.prop1 // => "value1"
}
}
4. proguardの設定
データバインドの際に使われるクラス名などをproguardの難読化の対象から外すため、proguard.cfgに下を追加する。
-keep class com.fasterxml.jackson.databind.** { *; }
-keepattributes *Annotation*,EnclosingMethod
-keep public class com.example.app1.model.** {
public void set*(***);
public *** get*();
}
1つめはjackson-databindのパッケージを、サブパッケージ含めて全て難読化対象外にするもの。
2つめはjacksonが使っているアノテーションとenclosing methodを難読化対象外にするもの。Javaではメソッドの中にローカルなクラスを定義できるらしく(enclosing method)、Jacksonがこれを使っている。jackson-databindのソースをgetEnclosingMethod
でgit grepすると、この機能(enclosing method)が使われているのが分かる。
3つめはMyModel1
などモデルクラスとそのget系, set系メソッドを難読化対象外にするもの。
5. apk生成
apkファイルを生成する際、 compile:dx フェーズでtrouble writing output: excess write of ...
というエラーが出た。
sbt> apk
...
trouble writing output: excess write of 16
[trace] Stack trace suppressed: run last App2/compile:dx for the full output.
[error] (App2/compile:dx) Nonzero exit value: 2
[error] Total time: 17 s, completed Feb 22, 2014 10:06:32 AM
project/Build.scala でanalysisBudget
を512から4096に増やすと解消した。こちらによると、Scalaコンパイラがソースコードの到達可能性・条件網羅性の検査(?)に最大どれくらい時間をかけるかというパラメータらしい...
object AndroidBuild extends Build {
...
val globalSettings = Seq (
...
initialize ~= { _ =>
sys.props("scalac.patmat.analysisBudget") = "4096"
}
)
lazy val main = AndroidProject (
...
settings = globalSettings
)
}
6. 動作確認
こちらのテスト用MainActivityを書いて実行したところ、無事に"value1"が表示された。