GoのYAML操作

プログラミング

YAMLは設定ファイルや外部のAPIや他システムと連携する際のデータフォーマットとしてよく用いられるフォーマットです。

プログラム内では受け取ったYAMLデータをシステム内に取り込んだり、出力したりするなどのYAMLデータの操作を行います。

本記事では、GoにおけるYAMLデータの操作についてまとめます。

記載しているプログラムは、Go1.15.2を使って動作確認をしています。

GoのYAML操作

GoのYAML操作について、以下の内容を解説します。

  • YAMLを扱うパッケージ
  • YAMLファイルの読み込み
  • YAMLデータの参照
  • YAMLデータの追加
  • YAMLデータの削除
  • 文字列からYAMLデータへの変換
  • YAMLデータから文字列への変換

YAMLを扱うパッケージ

GoでYAMLデータを扱うには、標準で提供されている組み込みパッケージには機能が存在しないため、外部のパッケージをインストールする必要があります。

最も有名でよく使われるのが、gopkg.in/yaml.v2 です。
gopkg.in/yaml.v2は、JSONデータを扱うencoding/jsonと似たような使い勝手となっていて使いやすくなっています。

gopkg.in/yaml.v2を利用するには、以下のように go get で取得します。

go get gopkg.in/yaml.v2

取得できたら、プログラム内でインポートします。

import (
    yaml "gopkg.in/yaml.v2"
)

なお、GoでYAMLデータを扱う際には、構造体のメンバにマッピングさせる方法とさせない方法があります。
YAMLデータの形式が決まっているのであれば、構造体にマッピングさせたほうが扱いやすいです。

今回は構造体にマッピングさせずに、自由にデータの追加や削除する方式で進めていきます。

YAMLファイルの読み込み

本記事では、以下のYAMLデータを扱います。
様々な形式のデータが含まれているテスト用のデータです。

string: 文字列
int: 2020
bool: true
null:
array:
- index: 1
  name: JavaScript
- index: 2
  name: Python
object:
  index: 3
  name: Java

上記の内容を data.yaml というファイルで保存し、プログラムから読み込みます。

ファイルから読み込んだYAMLデータ(文字列)を、Unmarshalを使って、mapを使った構造に変換します。
YAMLが入れ子になっている場合はmapが入れ子となり、並列で複数のデータがある場合はarrayに変換されます。

s, _ := ioutil.ReadFile("./data.yaml")

var yamldata map[string]interface{}
yaml.Unmarshal([]byte(s), &yamldata)
fmt.Println(yamldata)

上記のプログラムの結果は以下のようになります。

map[: array:[map[index:1 name:JavaScript] map[index:2 name:Python]] bool:true int:2020 object:map[index:3 name:Java] string:文字列]

YAMLデータの参照

mapとなったYAMLデータの参照は、通常のmapやarrayの参照方法と同じになります。
文字列や整数の参照は、直接キー名を指定して値を取得します。

fmt.Println(yamldata["string"]) // -> 文字列
fmt.Println(yamldata["int"]) // -> 2020
fmt.Println(yamldata["bool"]) // -> true
fmt.Println(yamldata["null"]) // -> <nil>

arrayの参照は、一旦スライスにキャストした後にインデックスを指定して参照します。

a := yamldata["array"].([]interface{})
fmt.Println((a[0].(map[interface{}]interface{}))["index"]) // -> 1
fmt.Println((a[0].(map[interface{}]interface{}))["name"]) // -> JavaScript
fmt.Println((a[1].(map[interface{}]interface{}))["index"]) // -> 2
fmt.Println((a[1].(map[interface{}]interface{}))["name"]) // -> Python

スライスなので、ループによる参照も可能です。

for _, value := range a {
    fmt.Println(value.(map[interface{}]interface{})["index"]) // -> 1 -> 2
    fmt.Println(value.(map[interface{}]interface{})["name"]) // -> JavaScript -> Python
}

オブジェクトの参照は、一旦mapにキャストした後に、ネストするオブジェクトのキー名を参照して値を取得することができます。

fmt.Println(yamldata["object"].(map[interface{}]interface{})["index"]) // -> 3
fmt.Println(yamldata["object"].(map[interface{}]interface{})["name"]) // -> Java

YAMLデータの追加

YAMLのmapにデータを追加するには、通常のmapやarrayへの追加方法と同じになります。

文字列や整数値の追加は、mapに直接キー名と値を指定します。

yamldata["string2"] = "文字列2"
yamldata["int2"] = 2021
yamldata["bool2"] = false
yamldata["null2"] = nil

arrayへの追加は、直接の追加ができないため、現在のarrayと追加したい情報をappendした新しいarrayで更新します。

yamldata["array"] = append(
    yamldata["array"].([]interface{}),
    map[string]interface{}{"index": 4, "name": "Ruby"})

オブジェクトを追加するには、オブジェクトをネストさせてキー名と値を指定します。

yamldata["object"].(map[interface{}]interface{})["object2"]
    = map[string]interface{}{"index": 5, "name": "Go"}

上記で追加したYAML全体を表示させると以下のようになります。

"": null
array:
- index: 1
  name: JavaScript
- index: 2
  name: Python
- index: 4
  name: Ruby
bool: true
bool2: false
int: 2020
int2: 2021
null2: null
object:
  index: 3
  name: Java
  object2:
    index: 5
    name: Go
string: 文字列
string2: 文字列2

YAMLデータの削除

YAMLのmapからデータを削除するのも、通常のmapやarrayから要素を削除する方法と同じになります。
arrayは直接削除ができないため、削除したい要素を取り除いた新しいarrayで更新します。

delete(yamldata, "string2")
delete(yamldata, "int2")
delete(yamldata, "bool2")
delete(yamldata, "null2")
yamldata["array"] = yamldata["array"].([]interface{})[0:2]
delete(yamldata["object"].(map[interface{}]interface{}), "object2")

上記で削除された結果のYAMLを表示させると以下のようになります。
(追加した分が削除されて、元の状態に戻っています)

"": null
array:
- index: 1
  name: JavaScript
- index: 2
  name: Python
bool: true
int: 2020
object:
  index: 3
  name: Java
string: 文字列

文字列からYAMLデータへの変換

文字列からYAMLのmapへ変換するには、 Unmarshal を使います。

str := "text: テキスト\nnum: 1"
var y map[string]interface{}
yaml.Unmarshal([]byte(str), &y)
fmt.Println(y) // -> map[num:1 text:テキスト]

YAMLデータから文字列への変換

YAMLのmapから文字列へ変換するには、Marshal を使います。

s, _ = yaml.Marshal(y)
fmt.Println(string(s))

上記のプログラムの結果は、以下のように改行されて表示されます。

num: 1
text: テキスト

まとめ

GoのYAML操作についてまとめると、以下のようになります。

  • GoでYAMLデータを扱うには、外部のパッケージが必要で、gopkg.in/yaml.v2が有名。
  • YAMLデータをプログラム内で扱うには、構造体にマッピングさせる方法と、mapやarrayを使って自由にデータの追加や削除をする方法がある。
  • gopkg.in/yaml.v2では、MarshalやUnmarshalによって、文字列とYAMLデータの相互変換を行う。

YAMLデータを扱う際にはぜひ使ってみてはいかがでしょうか。

 

今回は、GoのYAML操作について解説しました。

以上、参考になれば幸いです。

コメント

タイトルとURLをコピーしました