losizm / shampoo   1.0.0

Apache License 2.0 GitHub

The YAML library for Scala

Scala versions: 3.x

Shampoo

Maven Central

The YAML library for Scala.

Getting Started

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.

Lather Up!

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") }

Extracting Values

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") }

API Documentation

See scaladoc for additional details.

License

Shampoo is licensed under the Apache License, Version 2. See LICENSE for more information.