Summary
The following code is an example of reading a YAML file with the name specified by a command line flag.
$ cat members.yaml
members:
- name: "John Doe"
age: 20
privileged: false
languages:
- language: "Java"
years: 4
- language: "Go"
years: 2
- name: "Jane Doe"
age: 30
privileged: true
$ go run main.go -yaml-file members.yaml
name: John Doe
age: 20
privileged: false
language 0: Java for 4 year(s)
language 1: Go for 2 year(s)
name: Jane Doe
age: 30
privileged: true
Prerequisites
- go 1.18
Detail
- Parse a flag with the flag package
- Read a YAML file with the os package
- Convert YAML values into a Go struct with the yaml package
Parse a flag
Use flag package to parse command line flags.
In this case, the number of flags must be 1 and no arg is allowed.
var yamlFile string
flag.StringVar(&yamlFile, "yaml-file", "", "path to the YAML file")
flag.Parse()
if flag.NFlag() != 1 || flag.NArg() != 0 {
log.Fatalf("you must give one flag and no arg: #(flags)=%v, #(args)=%v\n", flag.NFlag(), flag.NArg())
}
if yamlFile == "" {
log.Fatalf("you must specify yaml file\n")
}
Read a file
os.Readfile can open a file to read.
// yamlFile: string
data, err := os.ReadFile(yamlFile)
if err != nil {
log.Fatalf("ERROR in reading file: %v\n", err)
}
Convert YAML values into a Go struct
yaml package converts YAML values into a Go struct.
In this case, assume the following YAML and define a struct representing values.
members:
- name: "John Doe"
age: 20
privileged: false
languages:
- language: "Java"
years: 4
- language: "Go"
years: 2
- name: "Jane Doe"
age: 30
privileged: true
Some fields can be omitted if there is no need to read it.map[interface{}]interface{}
can be used instead of the struct, but I do not like that way.
// data: []byte
y := struct {
Members []struct {
Name string `yaml:"name"`
Age int `yaml:"age"`
Privileged bool `yaml:"privileged"`
Languages []struct {
Language string `yaml:"language"`
Years int `yaml:"years"`
} `yaml:"languages"`
} `yaml:"members"`
}{}
err = yaml.Unmarshal(data, &y)
if err != nil {
log.Fatalf("ERROR in parsing YAML: %v\n", err)
}
Then the values can be accessed by the field name of the struct representing the key name.
fmt.Printf("name: %v\n", y.Members[0].Name)
(The following is the same content in Japanese.)
まとめ
コマンドラインで指定されたYAML
ファイルを開いて要素を取り出すまでのGo
コードは以下のように書けそう
(以下コピペ用)
$ cat members.yaml
members:
- name: "John Doe"
age: 20
privileged: false
languages:
- language: "Java"
years: 4
- language: "Go"
years: 2
- name: "Jane Doe"
age: 30
privileged: true
$ go run main.go -yaml-file members.yaml
name: John Doe
age: 20
privileged: false
language 0: Java for 4 year(s)
language 1: Go for 2 year(s)
name: Jane Doe
age: 30
privileged: true
環境
- go 1.18
詳細
- flagで指定されたYAMLファイル名をコード内で使用する
- 指定されたファイルを開く
- ファイルの内容を構造体にパースしてkeyで値を取り出せる状態にする
flagの処理
まずは標準のflagパッケージを使い、実行時に対象のファイル名を指定できるようにする。
4行目以降でflag数と入力値のバリデーションをかけてみたが、特に気にならなければ消してもいい。
var yamlFile string
flag.StringVar(&yamlFile, "yaml-file", "", "path to the YAML file")
flag.Parse()
if flag.NFlag() != 1 || flag.NArg() != 0 {
log.Fatalf("you must give one flag and no arg: #(flags)=%v, #(args)=%v\n", flag.NFlag(), flag.NArg())
}
if yamlFile == "" {
log.Fatalf("you must specify yaml file\n")
}
ファイルの読み込み
次にosパッケージで対象のファイルを開く。
// yamlFile: string
data, err := os.ReadFile(yamlFile)
if err != nil {
log.Fatalf("ERROR in reading file: %v\n", err)
}
YAMLにパース
最後にyamlパッケージで対象のyamlを構造体にパースする。
今回は下記のようなYAML
を想定し、
その構造をパース用の構造体に定義する。
members:
- name: "John Doe"
age: 20
privileged: false
languages:
- language: "Java"
years: 4
- language: "Go"
years: 2
- name: "Jane Doe"
age: 30
privileged: true
パースが不要な要素については定義しなくても問題ない。
また、構造体の代わりにmap[interface{}]interface{}
を使う方法もあるが、
要素を呼び出すたびに型をアサーションして使うのはあまり好きではない。
// data: []byte
y := struct {
Members []struct {
Name string `yaml:"name"`
Age int `yaml:"age"`
Privileged bool `yaml:"privileged"`
Languages []struct {
Language string `yaml:"language"`
Years int `yaml:"years"`
} `yaml:"languages"`
} `yaml:"members"`
}{}
err = yaml.Unmarshal(data, &y)
if err != nil {
log.Fatalf("ERROR in parsing YAML: %v\n", err)
}
構造体にパースできたら、
あとはフィールド名を指定して要素にアクセスができる。
fmt.Printf("name: %v\n", y.Members[0].Name)
おわり
久しぶりにGo
に触ったらいろいろ消耗してしまったので自分のコピペ用に書いてみた。
YAML
を設定ファイルとして読み込むものを書く機会が多いので、出番があるといいな…