How to read a YAML configuration file in Scala

This is an excerpt from the Scala Cookbook. This is Recipe 12.6, “How to read a YAML configuration file in Scala.”

Problem

You need to read a YAML configuration file in a Scala application.

Update

In Scala 2.12 and 2.13, BeanProperty can now be found as scala.beans.BeanProperty.

Solution

At the time of this writing, there are no custom Scala libraries for reading YAML files, so use the Java Snakeyaml library, as shown in the following example. Snakeyaml requires that a class conform to the JavaBeans specification, so the EmailAccount class uses the @BeanProperty annotation for all of its fields:

import org.yaml.snakeyaml.Yaml
import org.yaml.snakeyaml.constructor.Constructor
import scala.collection.mutable.ListBuffer
import scala.reflect.BeanProperty
import java.io.{File, FileInputStream}

object YamlBeanTest {
    def main(args: Array[String]) {
        val filename = "/foo/config.yaml"
        val input = new FileInputStream(new File(filename))
        val yaml = new Yaml(new Constructor(classOf[EmailAccount]))
        val e = yaml.load(input).asInstanceOf[EmailAccount]
        println(e)
    }
}

/**
* With the Snakeyaml Constructor approach shown in the main method,
* this class must have a no-args constructor.
*/
class EmailAccount {
    @BeanProperty var accountName = ""
    @BeanProperty var username = ""
    @BeanProperty var password = ""
    @BeanProperty var mailbox = ""
    @BeanProperty var imapServerUrl = ""
    @BeanProperty var protocol = ""
    @BeanProperty var minutesBetweenChecks = 0
    @BeanProperty var usersOfInterest = new java.util.ArrayList[String]()
    override def toString: String = s"acct: $accountName, user: $username, url: $imapServerUrl"
}

This code is written to read a YAML file with the following fields:

accountName: Ymail Account
username: johndoe
password: secret
mailbox: INBOX
imapServerUrl: imap.mail.yahoo.com
protocol: imaps
minutesBetweenChecks: 1
usersOfInterest: [barney, betty, wilma]

Discussion

You can also create a class with @BeanProperty fields as constructor arguments. The following example is a Scala version of the Java example shown in the Snakeyaml documentation. In this code, I load the sample data from a String (instead of a file) which is a nice feature of the Snakeyaml library that’s very useful for testing:

package yaml

import org.yaml.snakeyaml.Yaml
import scala.reflect.BeanProperty

object YamlBeanTest2 {
    def main(args: Array[String]) {
        val data = "--- !!yaml.Person [ David, Moore, 22 ]"
        val yaml = new Yaml
        val obj = yaml.load(data)
        val person = obj.asInstanceOf[Person]
        println(person)
    }
}

/**
* With the approach shown in the main method -- load() plus
* asInstanceOf -- this class must declare its properties in the
* constructor.
*/
// snakeyaml requires properties to be specified in the constructor
class Person(@BeanProperty var firstName: String,
             @BeanProperty var lastName: String,
             @BeanProperty var age: Int) {
    override def toString = s"$firstName, $lastName, $age"
}

See Also