読者です 読者をやめる 読者になる 読者になる

日頃の行い

個人的な日頃の行いをつらつら書いてます\\\\ ٩( 'ω' )و ////

Scalaのjson変換ツールspray-jsonを試してみた。

Scala

Scalaがとても楽しいので、spray-jsonによるScalaコード内でオブジェクトのjson変換を試してみました。

spray/spray-json · GitHub

基本的に上記のレポジトリのコードを元にしました。
ScalaのオブジェクトならDefaultJsonProtocolというトレイトを継承または、利用すれば変換することが出来るようです。
とても楽でした。

準備 /build.sbt

準備はbuild.sbtを以下のようにするだけです。

libraryDependencies += "io.spray" %%  "spray-json" % "1.3.1"

一般的なScalaオブジェクトをjsonに変換する

上記の準備だけで、toJsonを利用して、Scalaのオブジェクトをjson化することが出来ます。
Scalaで用意されたオブジェクト(Listなど)の変換には2通りのやり方があります。

  • 継承するパターン
  • traitを使うパターン

/src/main/com/Sample.scala

継承するパターン

package com

import spray.json._

object Sample extends DefaultJsonProtocol {

  def main(args: Array[String]) {
    val hoge:List[List[String]] = List(
      List("hoge", "fuga"),
      List("piyo", "fuga")
    )

  println(hoge.toJson)

  }
}

traitを使うパターン

package com

import spray.json._

object Sample extends App with DefaultJsonProtocol {

  val hoge:List[List[String]] = List(
    List("hoge", "fuga"),
    List("piyo", "fuga")
  )

  println(hoge.toJson)
}

どちらも出力は以下の様な感じです。

[["hoge","fuga"],["piyo","fuga"]]

エラーが出るとき

[info] Compiling 1 Scala source to /Users/a-tanaka/Documents/scala_workspace/spray-json/target/scala-2.10/classes...
[error] /Users/a-tanaka/Documents/scala_workspace/spray-json/src/main/scala/com/Sample.scala:12: Cannot find JsonWriter or JsonFormat type class for List[List[String]]
[error]   println(hoge.toJson)
[error]                ^
[error] one error found
[error] (compile:compile) Compilation failed
[error] Total time: 0 s, completed 2015/02/11 1:38:59

DefaultJsonProtocolを利用していないために、こんなエラーが出ます。
traitとしてwithでDefaultJsonProtocolを呼ぶか、DefaultJsonProtocolをextendsしてあげるかしてあげましょう。

caseクラスをjson化する

spray/spray-json · GitHub に例としてあがっているものを利用します。

case class Color(name: String, red: Int, green: Int, blue: Int)

object MyJsonProtocol extends DefaultJsonProtocol {
  implicit val colorFormat = jsonFormat4(Color)
}

import MyJsonProtocol._

val json = Color("CadetBlue", 95, 158, 160).toJson
val color = json.convertTo[Color]

case Classを利用している場合は、jsonFormatという関数が用意されているようです。
コンストラクタの引数が4つの場合上記のようにjsonFormat4を利用すれば良いようです。
つまり、case Classのコンストラクタの引数がNの時、jsonFormatNを利用すれば良いらしいです。

caseクラス以外を利用する

implicitオブジェクトを定義する

class Color(val name: String, val red: Int, val green: Int, val blue: Int)

object MyJsonProtocol extends DefaultJsonProtocol {
  implicit object ColorJsonFormat extends RootJsonFormat[Color] {
    def write(c: Color) =
      JsArray(JsString(c.name), JsNumber(c.red), JsNumber(c.green), JsNumber(c.blue))

    def read(value: JsValue) = value match {
      case JsArray(Vector(JsString(name), JsNumber(red), JsNumber(green), JsNumber(blue))) =>
        new Color(name, red.toInt, green.toInt, blue.toInt)
      case _ => deserializationError("Color expected")
    }
  }
}

import MyJsonProtocol._

val json = Color("CadetBlue", 95, 158, 160).toJson
val color = json.convertTo[Color]

writeという関数でjsonに変換する処理を書いて、
readという関数でjsonから元となるクラスへの変換処理を書けば良いみたいです。

もし、List[List[String]]というクラスをjsonに変換したい場合以下の様なコードを書けばよいかと思います。(read関数は適当です。)

package com

import spray.json._

object Sample extends App with DefaultJsonProtocol {
  implicit object NestedListFormat extends RootJsonFormat[List[List[String]]] {
    def write (nestedListFormat:List[List[String]]) =
      JsArray(
        nestedListFormat.map({ list =>
          JsArray(list.map({ str => JsString(str) }).toVector)
        }).toVector
      )

    def read (c:JsValue):List[List[String]] = List(List(""))
  }

  val hoge:List[List[String]] = List(
    List("hoge", "fuga"),
    List("piyo", "fuga")
  )

  println(hoge.toJson)
}

toJsonでjson化されるのはかなり楽でいいですね。