Scala: Parse a Yaml file using SnakeYaml

9.5k views Asked by At

I am pretty new to scala. I was trying to parse a Yaml File using snakeyaml in scala. I am getting the data but it is in the form of an object. I can convert the object ot string but it defeats the whole purpose of using the Yaml.

e.g. the file i am using is "abcd.yaml" with data

aa:
  - x
  - y
bb: z

my code goes like this:

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

def parseYaml(){
val ios = new FileInputStream(new File("abcd.yaml"))
val yaml = new Yaml()
val obj = yaml.load(ios)
}

but here I am getting an object and I cannot use the values inside. Any solution?

2

There are 2 answers

0
rrawat On BEST ANSWER

I got the solution, it's to cast to java maps instead of scala maps:

Just use asInstanceOf[java.util.Map[String, Any] and it works like a charm. The solution is to call load like this:

    val obj = yaml.load(ios).asInstanceOf[java.util.Map[String, Any]]
0
Abhishek Kanaparthi On

In order to unpack a single level YAML file like

aa:
 - x
 - y
bb: z

The solution would be to cast it to a data type that you expect to receive after you unpack the YAML file, i.e, Map[, ], generally the most preferred is Map[String, Any] to accommodate to any datatype retrieved.

val obj = yaml.load(ios).asInstanceOf[java.util.Map[String, Any]]

This is the simplest solution, however, this method becomes difficult to use when you have multiple levels in YAML file, then you will have to cast the new object to a Map/List, every time you step a level deeper. Eg YAML file:

aa:
 - x
 - y
bb: z
cc:
  p: abc
  q: 123
  r: true
  s:
    - listItem1
    - listItem2
    - listItem3

In such a case, relying on SnakeYAML to parse the file and relying on Jackson YAML to traverse through the file is best, owing to the flexibility that we have in Jackson's JsonNode datatype

import java.io.{File, FileInputStream, FileReader}
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.fasterxml.jackson.databind.{JsonNode, ObjectMapper}
import org.yaml.snakeyaml.Yaml    

// Parsing the YAML file with SnakeYAML - since Jackson Parser does not have Anchors and reference support
val ios = new FileInputStream(new File(yamlFilePath))
val yaml = new Yaml()
val mapper = new ObjectMapper().registerModules(DefaultScalaModule)
val yamlObj = yaml.loadAs(ios, classOf[Any])
    
// Converting the YAML to Jackson YAML - since it has more flexibility
val jsonString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(yamlObj) // Formats YAML to a pretty printed JSON string - easy to read
val jsonObj = mapper.readTree(jsonString)

JsonNode can be parsed using JsonNode's at or get methods:

jsonObj.at("/cc/r").asBoolean // Returns boolean of value in r
jsonObj.get("bb") // Returns the value in key bb

Leveraging asText, asBoolean, asInt, etc. methods in JsonNode will ease the process of data extraction & typecasting process