The YAML library for Scala.
To get started, add Shampoo to your project:
libraryDependencies += "com.github.losizm" %% "shampoo" % "1.0.0"
The underlying YAML processor is provided by SnakeYAML Engine, so it'll be added transitively.
Reading and writing are powered by YamlConstructor
and YamlRepresenter
.
Library-provided implementations are available for working with standard types,
such as String
, Int
, etc. You must define custom implementations to work
with other types.
import scala.language.implicitConversions
import shampoo.yaml.{ *, given }
case class User(id: Int, name: String, groups: Seq[String])
// Define how to construct User from YAML
given YamlConstructor[User] with
def construct(yaml: YamlNode) =
User(
yaml("id"),
yaml("name"),
yaml("groups")
)
// Load YAML mapping
val yaml = Yaml.load("""
id: 1000
name: lupita
groups:
- lupita
- admin
- sudoers
""")
// Construct and verify
val user = yaml.as[User]
assert(user.id == 1000)
assert(user.name == "lupita")
assert(user.groups == Seq("lupita", "admin", "sudoers"))
// Define how to represent YAML from User
given YamlRepresenter[User] with
def represent(user: User) =
Yaml.map(
"id" -> user.id,
"name" -> user.name,
"groups" -> user.groups
)
// Represent and verify
val yamlUser = Yaml.toYaml(user)
assert(yamlUser.getInt("id") == 1000)
assert(yamlUser.getString("name") == "lupita")
assert(yamlUser("groups").as[Seq[String]] == Seq("lupita", "admin", "sudoers"))
Special implementations are available for working with collections. For example,
if YamlConstructor[User]
is defined, YamlConstructor[Seq[User]]
can be
inferred. The same applies to YamlRepresenter[User]
and YamlRepresenter[Seq[User]]
.
// Load YAML sequence
val yaml = Yaml.load("""
- id: 0
name: root
groups:
- root
- id: 1000
name: lupita
groups:
- lupita
- admin
- sudoers
""")
// Read YamlSequence as Seq[User]
val users = yaml.as[Seq[User]]
assert { users(0) == User(0, "root", Seq("root")) }
assert { users(1) == User(1000, "lupita", Seq("lupita", "admin", "sudoers")) }
// Or as other Iterables
val userList = yaml.as[List[User]]
val userSet = yaml.as[Set[User]]
// Even as Array
val userArray = yaml.as[Array[User]]
// Write Seq[User] to YamlSequence
val yamlUsers = Yaml.toYaml(users)
assert { yamlUsers(0).getInt("id") == 0 }
assert { yamlUsers(0).getString("name") == "root" }
assert { yamlUsers(0)("groups").as[Seq[String]] == Seq("root") }
assert { yamlUsers(1).getInt("id") == 1000 }
assert { yamlUsers(1).getString("name") == "lupita" }
assert { yamlUsers(1)("groups").as[Seq[String]] == Seq("lupita", "admin", "sudoers") }
You can traverse YamlMapping
and YamlSequence
to extract nested values. The \
extension method makes this clean and easy.
import scala.language.implicitConversions
import shampoo.yaml.{ *, given }
case class User(id: Int, name: String)
// Define how to construct User
given YamlConstructor[User] =
yaml => User(yaml("id"), yaml("name"))
val yaml = Yaml.load("""
node:
name: localhost
users:
- id: 0
name: root
- id: 1000
name: lupita
""")
// Get users array from node object
val users = (yaml \ "node" \ "users").as[Seq[User]]
// Get first user (at index 0) in users array
val user = (yaml \ "node" \ "users" \ 0).as[User]
// Get name of second user (at index 1) in users array
val name = (yaml \ "node" \ "users" \ 1 \ "name").as[String]
And, just as easy, you can do a recursive lookup with \\
to collect values by
key.
// Get all "name" values
val names = (yaml \\ "name").map(_.as[String])
assert { names == Seq("localhost", "root", "lupita") }
See scaladoc for additional details.
Shampoo is licensed under the Apache License, Version 2. See LICENSE for more information.