Read dynamic type values from YAML in Golang
GolangでYAMLを読み込む際、要素の型が実行時に決まる場合を考えます。例えば、以下のYAMLでは values
で数値、文字列、マップの値が列挙されています。
values: - 100 - foo - key: value
このようなYAMLを読み込む場合、型宣言を interface{}
にすると実行時に型をチェックして値を処理できます。
type Spec struct { Values []interface{} `yaml:"values"` } func main() { spec := Spec{} yaml.NewDecoder(file).Decode(&spec) for _, value := range spec.Values { switch typedValue := value.(type) { case string: // 文字列を表示する fmt.Printf("string=%s\n", typedValue) case int: // 数値を表示する fmt.Printf("int=%d\n", typedValue) case map[interface{}]interface{}: // マップに含まれる key の値を表示する fmt.Printf("key=%+v\n", typedValue["key"]) } } }
この方法を応用するとYAMLの部分木を出力することも可能です。先ほどのYAMLに対して以下を実行すると key: value
が表示されます。
for _, value := range spec.Values { case map[interface{}]interface{}: // 部分木を表示する e := yaml.NewEncoder(os.Stdout) defer e.Close() e.Encode(typedValue) }
Full example
package main import ( "fmt" "os" "gopkg.in/yaml.v2" ) type Spec struct { Values []interface{} `yaml:"values"` } func main() { file, err := os.Open("fixture.yaml") if err != nil { panic(err) } spec := Spec{} yaml.NewDecoder(file).Decode(&spec) for _, value := range spec.Values { switch typedValue := value.(type) { case string: fmt.Printf("string=%s\n", typedValue) case int: fmt.Printf("int=%d\n", typedValue) case map[interface{}]interface{}: e := yaml.NewEncoder(os.Stdout) defer e.Close() e.Encode(typedValue) } } }