【Scala】Scala ~ YAML ~

■ はじめに

Scala で YAMLファイルを扱う方法を扱う

目次

【1】YAML用ライブラリ「org.yaml.snakeyaml.Yaml」
 1)設定
 2)API仕様
【2】サンプル
 例1:Hello World
 例2:独自クラスにマッピングする
 例3:別YAML&リファクタリング

【1】YAML用ライブラリ「org.yaml.snakeyaml.Yaml

* YAML用ライブラリとして「org.yaml.snakeyaml.Yaml」を使用
 => 詳細は、以下を参照

https://javadoc.io/static/org.yaml/snakeyaml/1.19/org/yaml/snakeyaml/Yaml.html
https://javadoc.io/static/org.yaml/snakeyaml/2.0/org/yaml/snakeyaml/Yaml.html

1)設定

* 使用には、libraryDependencies への追加が必要。

build.sbt

libraryDependencies ++= Seq(
  "org.yaml" % "snakeyaml" % "1.16"
)

https://mvnrepository.com/artifact/org.yaml/snakeyaml

2)API仕様

* 以下を参照

https://javadoc.io/static/org.yaml/snakeyaml/1.19/org/yaml/snakeyaml/Yaml.html

【2】サンプル

「例2:独自クラスにマッピングする」「例3:別YAML&リファクタリング」
を汎用的に独自クラスをマッピングする実装は、以下の関連記事を参照

https://dk521123.hatenablog.com/entry/2023/03/21/003817

例1:Hello World

input.yaml

names:
  - abc
  - def
age: 10

Main.scala

import org.yaml.snakeyaml.Yaml
import java.io.{File, FileInputStream}
import scala.jdk.CollectionConverters._

object Main {
  val yaml = new Yaml
  val input = new FileInputStream(new File("input.yaml"))
  val inputData = yaml.load(input)
  val data = inputData.asInstanceOf[java.util.Map[String, Any]]
  print(data.toString)
  val map = data.asScala
  print(map.get("names"))
  print(map.get("age"))
  for(value <- map) {
    print(value)
  }
}

例2:独自クラスにマッピングする

https://alvinalexander.com/scala/how-to-read-yaml-configuration-file-in-scala/

を参考にしたが
「import scala.reflect.BeanProperty」⇒「import scala.beans.BeanProperty」
にしないと動かなかった。

user.yaml

id: 100
name: Mike
hobbies:
  - "running"
  - "programming"
  - "football"
skills:
  "Rust": null
  "Python": True

Main.scala

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

object Main extends App {
  val input = new FileInputStream(new File("user.yaml"))
  val inputYaml = new Yaml(new Constructor(classOf[User]))

  // val user = inputYaml.load(input).asInstanceOf[User]
  val user = inputYaml.loadAs(input, classOf[User])

  // id: 100, name: Mike, hobbies: [running, programming, football], skills: {Rust=null, Python=true}
  println(user)
}

class User {
  @BeanProperty var id = -1
  @BeanProperty var name = ""
  @BeanProperty var hobbies = new java.util.ArrayList[String]()
  @BeanProperty var skills = new java.util.HashMap[String, Any]()
    override def toString: String =
      s"id: $id, name: $name, hobbies: $hobbies, skills: $skills"
}

例3:別YAMLリファクタリング

process_info.yaml

processName: processA
schedule: 10:00-12:00
emailListToNotify:
  - sample@gmail
  - sample@yahoo.com
subProcesses:
  sub-processA1:
    inputPath: s3://your-bucket/sub-processA1/input/
    outputPath: s3://your-bucket/sub-processA1/output/
  sub-processA2:
    inputPath: s3://your-bucket/sub-processA2/input/
    outputPath: s3://your-bucket/sub-processA2/output/
  sub-processA3:
    inputPath: s3://your-bucket/sub-processA3/input/
    outputPath: s3://your-bucket/sub-processA3/output/

Main.scala

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

object Main extends App {
  val inputPath = raw"C:\work\scala\codes\HelloWorld\thisismyfirsttime\src\main\scala\process_info.yaml"
  val processInfo = readYaml(inputPath)
  println(processInfo)

  def readYaml(inputPath: String): ProcessInfo = {
    var returnValue: ProcessInfo = new ProcessInfo
    Using(new FileInputStream(new File(inputPath))) { reader =>
      val inputYaml = new Yaml(new Constructor(classOf[ProcessInfo]))
      returnValue = inputYaml.loadAs(reader, classOf[ProcessInfo])
    }
    return returnValue
  }
}

class ProcessInfo {
  @BeanProperty var processName = ""
  @BeanProperty var schedule = ""
  @BeanProperty var emailListToNotify =
    new java.util.ArrayList[String]()
  @BeanProperty var subProcesses =
    new java.util.HashMap[String, java.util.HashMap[String, Any]]()
  override def toString: String =
      s"processName : $processName , schedule: $schedule, emailListToNotify : $emailListToNotify , subProcesses : $subProcesses "
}

補足

* Usingなどのファイルハンドリング関連については、
 以下の関連記事を参照のこと

Scala ~ ファイルハンドリング ~
https://dk521123.hatenablog.com/entry/2023/01/03/000000

参考文献

https://qiita.com/toastkidjp/items/66ddd8daa3c625afc351
https://www.qoosky.io/techs/66d656fb42
https://alvinalexander.com/scala/how-to-read-yaml-configuration-file-in-scala/
https://www.task-notes.com/entry/20150923/1442977200

関連記事

Scala ~ 環境構築編 ~
https://dk521123.hatenablog.com/entry/2023/03/10/193805
Scala ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2023/03/12/184331
Scala ~ 基本編 / コレクション ~
https://dk521123.hatenablog.com/entry/2023/03/13/000345
Scala ~ 基本編 / クラス ~
https://dk521123.hatenablog.com/entry/2023/03/14/000857
Scala ~ 基本編 / ジェネリック
https://dk521123.hatenablog.com/entry/2023/03/21/003817
Scala ~ ファイルハンドリング ~
https://dk521123.hatenablog.com/entry/2023/01/03/000000
ScalaJSON
https://dk521123.hatenablog.com/entry/2023/04/04/000733