先ほどjson4sを試してみましたが、
json4sで遊ぶ
http://d.hatena.ne.jp/Kazuhira/20140419/1397895464
せっかくなのでJacksonのScalaモジュールも試してみたくなりまして。
jackson-module-scala
https://github.com/FasterXML/jackson-module-scala
こちらは、Jackson本家のScalaサポートになります。メンテナンス状況は大丈夫かなぁ?と思ったら、JacksonとScalaの最新(さすがに2.11はまだですが)に追従していっているので見ておくべきかなと。
では、いってみましょう。
準備
とりあえず、依存関係の定義を。
build.sbt
name := "jackson-module-example" version := "0.0.1-SNAPSHOT" scalaVersion := "2.10.4" organization := "org.littlewings" scalacOptions ++= Seq("-Xlint", "-deprecation", "-unchecked") incOptions := incOptions.value.withNameHashing(true) libraryDependencies ++= Seq( "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.3.3", "org.scalatest" %% "scalatest" % "2.1.3" % "test" )
では、これに対してテストを書いていく感じで。
src/test/scala/org/littlewings/jackson/JacksonModuleSpec.scala
package org.littlewings.jackson import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.scala.DefaultScalaModule import org.scalatest.FunSpec import org.scalatest.Matchers._ class JacksonModuleSpec extends FunSpec { describe("JacksonModule") { // ここに、テストを書く! } }
今回のサンプルでは、importするのは
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.scala.DefaultScalaModule
だけでよさそうです。
Case ClassをJSONと相互変換する
今回は、いきなりCase Classから。先ほどのjson4sの時と、同じCase Classを用意しました。
src/main/scala/org/littlewings/jackson/CaseClasses.scala
package org.littlewings.jackson case class Organization(name: String, persons: List[Person]) case class Person(name: String, age: Int)
JSON文字列からCase Classへの変換。
it("parse json as Case Class") { val json = """|{"name":"some organization", | "persons": | [{"name":"Taro","age":22}, | {"name":"Hanako","age":18}, | {"name":"Saburo","age":25}]}""".stripMargin val mapper = new ObjectMapper mapper.registerModule(DefaultScalaModule) mapper.readValue(json, classOf[Organization]) should be (Organization("some organization", List(Person("Taro", 22), Person("Hanako", 18), Person("Saburo", 25)))) }
Scalaモジュールを使う時は、JacksonのObjectMapperにScalaModuleを登録すればよいみたいです。
val mapper = new ObjectMapper mapper.registerModule(DefaultScalaModule)
あとは、普通にObjectMapperとして使用します。
mapper.readValue(json, classOf[Organization]) should be (Organization("some organization", List(Person("Taro", 22), Person("Hanako", 18), Person("Saburo", 25))))
Case ClassからJSONへの変換。
it("produce json from Case Class") { val organization = Organization( "some organization", List(Person("Taro", 22), Person("Hanako", 18), Person("Saburo", 25)) ) val mapper = new ObjectMapper mapper.registerModule(DefaultScalaModule) mapper.writeValueAsString(organization) should be { """{"name":"some organization","persons":[{"name":"Taro","age":22},{"name":"Hanako","age":18},{"name":"Saburo","age":25}]}""" } }
ObjectMapperに対する設定は、JSONからCase Classに戻した時と同じです。
もうちょっとミュータブルな例で
ここで、json4sではうまくいかなかった、以下のようなクラスに対するマッピング。
src/main/scala/org/littlewings/jackson/SimpleClasses.scala
package org.littlewings.jackson class VarPerson { var name: String = _ var age: Int = _ }
こちらだと、普通にうまくいきました。
it("parse json use Var Class") { val json = """|{ "name": "Taro", | "age": 22}""".stripMargin val mapper = new ObjectMapper mapper.registerModule(DefaultScalaModule) val person = mapper.readValue(json, classOf[VarPerson]) person.name should be ("Taro") person.age should be (22) } it("produce json from use Var Class") { val person = new VarPerson person.name = "Taro" person.age = 22 val mapper = new ObjectMapper mapper.registerModule(DefaultScalaModule) mapper.writeValueAsString(person) should be ("""{"name":"Taro","age":22}""") }
Scalaのコレクションを使う
it("parse json use Scala Collection") { val json = """|{"name":"some organization", | "persons": | [{"name":"Taro","age":22}, | {"name":"Hanako","age":18}, | {"name":"Saburo","age":25}]}""".stripMargin val mapper = new ObjectMapper mapper.registerModule(DefaultScalaModule) mapper.readValue(json, classOf[Map[_, _]]) should be { Map("name" -> "some organization", "persons" -> List( Map("name" -> "Taro", "age" -> 22), Map("name" -> "Hanako", "age" -> 18), Map("name" -> "Saburo", "age" -> 25) ) ) } }
it("produce json from Scala Collection") { val organization = Map("name" -> "some organization", "persons" -> List( Map("name" -> "Taro", "age" -> 22), Map("name" -> "Hanako", "age" -> 18), Map("name" -> "Saburo", "age" -> 25) ) ) val mapper = new ObjectMapper mapper.registerModule(DefaultScalaModule) mapper.writeValueAsString(organization) should be { """{"name":"some organization","persons":[{"name":"Taro","age":22},{"name":"Hanako","age":18},{"name":"Saburo","age":25}]}""" } } }
これこれで、導入は簡単そうで良いと思います。