Search code examples
scalajacksonyamlsnakeyaml

Reading YAML in Scala without 'case class'


I am trying to read YAML file from Scala and I am able to read the file using the code given below. One of the disadvantage I see here is the necessity for me to create case class to have a mapping with the YAML file I am using. Every time I change the content of YAML it becomes necessary for me to change the case class. Is there any way in Scala to read YAML with out the need for me to create the case class. (I have also used Python to read YAML ; where we do not have the constraint of mapping a Class with the YAML structure...and would like to do similar thing in Scala)

Note : When I add a new property in YAML and if my case class do not have a matching property I get UnrecognizedPropertyException

package yamlexamples

import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.scala.DefaultScalaModule

object YamlTest extends App{


  case class Prop(country: String, state: List[String])

  val mapper: ObjectMapper = new ObjectMapper(new YAMLFactory())

  mapper.registerModule(DefaultScalaModule)
  val fileStream = getClass.getResourceAsStream("/sample.yaml")
  val prop:Prop = mapper.readValue(fileStream, classOf[Prop])

  println(prop.country + ", " + prop.state)

}

sample.yaml(This works with code)

country: US
state: 
  - TX
  - VA

sample.yaml (This throws Exception)

country: US
state: 
  - TX
  - VA
continent: North America

Solution

  • You could parse the Yaml file and load it as a collections object instead of case case. But this comes at the cost of losing typesafety in your code. The Below code uses the load function supported by Yaml. Note that the load has overloaded methods to read from a inputstream/reader as well..

    import scala.collection.JavaConverters._
    val yaml = new Yaml()
    val data = yaml.load(
          """
            |country: US
            |state:
            |  - TX
            |  - VA
            |continent: North America
          """.stripMargin).asInstanceOf[java.util.Map[String, Any]].asScala
    

    Now data is a scala mutable collection instead of a case class

    data: scala.collection.mutable.Map[String,Any] = Map(country -> US, state -> [TX, VA], continent -> North America)