argonautを使ってみた

設定をXMLよりももっと簡単に記述して読み込みたい、という希望のもとにあれこれ調べるまでもなく、実際Jsonを使ってやってみるのが標準のようだ。Scala標準のJsonパーサーはどうやら使いにくいし今後無くなる方向だそうです。(こちら参照→標準ライブラリのJSONはScala2.11から非推奨になるので使ってる人は今すぐ使うのやめましょう - xuwei-k's blog


関数型言語を勉強してるのなら、argonaut一択でしょう。と使い始めて、まず6.0.4ではScalazの7.1に対応していないので6.1-M6とか使いましょう。sbtではこんなふうに。

libraryDependencies ++= Seq("io.argonaut" %% "argonaut" % "6.1-M4")

また、Scalazのレポジトリをresolversに入れておかないと見つからないかもしれない。上記だけではsbtのupdate時にエラーになるときは、これも追加しましょう。

resolvers ++= Seq("Scalaz Bintray Repo" at "http:/dl.bintray.com/scalaz/releases")


多分これでargonautのquickstart→Argonaut: Purely Functional JSON in Scalaにある以下のがうまく動くはず。
カンマとか一個でも抜けてると動かないので細心の注意が必要です。

/* Below source code is copied from argonaut official page http://argonaut.io/doc/quickstart/ */
package argonaut.doc
 
import scalaz._, Scalaz._
import argonaut._, Argonaut._
 
 
object QuickStartExample extends App {
  val input = """
    [
      { "name": "Mark", "age": 191 },
      { "name": "Fred", "age": 33, "greeting": "hey ho, lets go!" },
      { "name": "Barney", "age": 35, "address": {
        "street": "rock street", "number": 10, "post_code": 2039
      }}
    ]
  """
 
  // parse the string as json, attempt to decode it to a list of person,
  // otherwise just take it as an empty list.
  val people = input.decodeOption[List[Person]].getOrElse(Nil)
 
  // work with your data types as you normally would
  val nice = people.map(person =>
    person.copy(greeting = person.greeting.orElse(Some("Hello good sir!"))))
 
  // convert back to json, and then to a pretty printed string, alternative
  // ways to print may be nospaces, spaces2, or a custom format
 
  val result = nice.asJson
  println(result.spaces4)
 
  assert(result.array.exists(_.length == 3))
}
 
 
case class Address(street: String, number: Int, postcode: Int)
 
object Address {
  // Define codecs easily from case classes
  implicit def AddressCodecJson: CodecJson[Address] =
    casecodec3(Address.apply, Address.unapply)("street", "number", "post_code")
}
 
case class Person(name: String, age: Int, address: Option[Address], greeting: Option[String])
 
object Person {
  implicit def PersonCodecJson: CodecJson[Person] =
    casecodec4(Person.apply, Person.unapply)("name", "age", "address", "greeting")
}