Scala で YAML を読み込む方法 (の代替)
[履歴] [最終更新] (2016/02/21 22:44:33)

インストール

2016-2-9 現在のところ Scala で YAML を扱うこなれた方法はないようです。ここでは SnakeYAML を利用します。

build.sbt (sbt)

libraryDependencies += "org.yaml" % "snakeyaml" % "1.16"

pom.xml (maven)

<dependency>
  <groupId>org.yaml</groupId>
  <artifactId>snakeyaml</artifactId>
  <version>1.16</version>
</dependency>

サンプルコード

config.yaml (UTF-8)

--- !!myapp.Person
[
  鈴木, # 名字
  一郎, # 名前
  123, # 年齢
]

Main.scala

package myapp

import org.yaml.snakeyaml.Yaml
import scala.beans.BeanProperty
import java.io.{File, FileInputStream}

class Person(
  @BeanProperty var firstName: String,
  @BeanProperty var lastName: String,
  @BeanProperty var age: Int) {
  override def toString: String = {
    return firstName + ", " + lastName + ", " + age
  }
}

object Main {
  def main(args: Array[String]): Unit = {
    val yaml = new Yaml
    val input = new FileInputStream(new File("config.yaml"))
    val obj = yaml.load(input)
    val person = obj.asInstanceOf[Person]
    println(person) //=> 鈴木, 一郎, 123
    println(person.getAge()) //=> 123
  }
}

Scala コードを Eval

設定ファイルとしての YAML を扱うことが難しい場合は、Scala コードを実行時に Eval することで代替できます。少し前までは Twitter 社が提供する util-eval を用いて Eval を実現していましたが、2.11 では Scala 公式のライブラリで Eval がサポートされています。sbt で依存関係を設定すると使用できるようになります。Scala 2.11.7 の場合は以下のようにします。Eval は重たい処理ですので頻繁に実行することは避けるようにします。

libraryDependencies += "org.scala-lang" % "scala-compiler" % "2.11.7"

Main.scala

package myapp

import scala.tools.reflect.ToolBox
import scala.reflect.runtime.currentMirror

trait Config {
  val hostname: String
  val port: Int
}

object Main {
  def main(args: Array[String]): Unit = {

    val toolbox = currentMirror.mkToolBox()

    // 簡単な例
    val source: String = "List(1,2)"
    val tree = toolbox.parse(source)
    val result = toolbox.eval(tree).asInstanceOf[List[Int]]
    println(result) //=> List(1, 2)

    // 外部ファイルを利用する例

    // 既存のクラス List[Int]
    val listConfig = toolbox.eval(toolbox.parse(scala.io.Source.fromFile("./Config1.dat").mkString)).asInstanceOf[List[Int]]
    println(listConfig) //=> List(1, 2, 3)

    // 独自のクラス Config
    val config: Config = toolbox.eval(toolbox.parse(scala.io.Source.fromFile("./Config2.dat").mkString)).asInstanceOf[Config]
    println(config.hostname) //=> localhost
    println(config.port) //=> 80
  }
}

Config1.dat

List(1,2,3)

Config2.dat
文法としては、トレイトをミックスインした無名クラスです。

new myapp.Config {
  val hostname = "localhost"
  val port = 80
}
関連ページ