Golang嵌套了Yaml值

时间:2016-07-11 15:35:15

标签: go yaml

我正在尝试访问Yaml文件并获取单个值,但我正在努力使用Struct语法来实现此目的。下面的代码处理Yaml,我可以打印完整的Struct,但是如何访问单个ecs.services.name属性?

关于如何处理这个问题的任何建议都是值得欢迎的,因为我遇到了几个Yaml图书馆但是还没有能够让它们完全发挥作用。

test.yaml:

ecs:
  services:
    - name: my-service
      taskDefinition: my-task-def
      desiredCount: 1

Yaml.go

package main

import (
    "fmt"
    "io/ioutil"
    "path/filepath"

    "gopkg.in/yaml.v2"
)

type Config struct {
    //Ecs []map[string]string this works for ecs with name
    Ecs struct {
        Services []struct {
            Name           string
            TaskDefinition string
            DesiredCount   int
        }
    }
    //Services []map[string][]string
}

func main() {
    filename, _ := filepath.Abs("test.yaml")

    yamlFile, err := ioutil.ReadFile(filename)
    check(err)

    var config Config

    err = yaml.Unmarshal(yamlFile, &config)
    check(err)

    fmt.Printf("Description: %#v\n", config.Ecs.Services)
}

func check(e error) {
    if e != nil {
        panic(e)
    }
}

输出

$ go run yaml.go
Description: []struct { Name string; TaskDefinition string; DesiredCount int }{struct { Name string; TaskDefinition string; DesiredCount int }{Name:"my-service", TaskDefinition:"", DesiredCount:0}}

3 个答案:

答案 0 :(得分:3)

我有一个类似的要求,在yaml文件中我需要执行嵌套检索。因为我发现没有开箱即用的解决方案我必须自己写。

我有一个yaml文件,内容如下

"a": "Easy!"
"b":
  "c": "2"
  "d": ["3", "4"]
"e":
  "f": {"g":"hi","h":"6"}

我想从此结构访问和打印嵌套值,输出应如下所示

--- yaml->a: Easy!
--- yaml->b->c: 2
--- yaml->b->x: None  //not existing in the yaml
--- yaml->y->w: None  //not existing in the yaml
--- yaml->b->d[0]: 3   //accessing value from a list
--- yaml->e->f->g: hi 

我也不想定义一个结构来保存解析后的yaml。 golang中最通用的结构是interface {}。解组yaml的最合适的结构是map[interface{}]interface{}。对于来自java的人来说,这类似于Map<Object,Object>。一旦数据被解组,我必须编写一个函数,它可以使用嵌套键遍历结构并返回值。

以下是执行此操作的代码。打开注释并执行以了解代码如何遍历嵌套结构并最终获取值。虽然这个例子假设yaml中的所有值都是字符串,但这也可以扩展为数字键和值。

package main

import (
    "fmt"
    "gopkg.in/yaml.v2"
    "io/ioutil"
    "reflect"
)

func main() {

    testFile := "test.yaml"
    testYaml, rerr := ioutil.ReadFile(testFile)
    if rerr != nil {
        fmt.Errorf("error reading yaml file: %v", rerr)
    }

    m := make(map[interface{}]interface{})
    if uerr := yaml.Unmarshal([]byte(testYaml), &m); uerr != nil {
        fmt.Errorf("error parsing yaml file: %v", uerr)
    }

    fmt.Printf("--- yaml->a: %v\n\n", getValue(m, []string{"a"}, -1))         //single value in a map
    fmt.Printf("--- yaml->b->c: %v\n\n", getValue(m, []string{"b", "c"}, -1)) //single value in a nested map
    fmt.Printf("--- yaml->b->x: %v\n\n", getValue(m, []string{"b", "x"}, -1)) //value for a non existent nest key
    fmt.Printf("--- yaml->y->w: %v\n\n", getValue(m, []string{"y", "w"}, -1)) //value for a non existent nest key
    fmt.Printf("--- yaml->b->d[0]: %v\n\n", getValue(m, []string{"b", "d"}, 0))
    fmt.Printf("--- yaml->e->f->g: %v\n\n", getValue(m, []string{"e", "f", "g"}, -1))
}

func getValue(obj map[interface{}]interface{}, keys []string, indexOfElementInArray int) string {

    //fmt.Printf("--- Root object:\n%v\n\n", obj)
    value := "None"
    queryObj := obj
    for i := range keys {
        if queryObj == nil {
            break
        }
        if i == len(keys)-1 {
            break
        }
        key := keys[i]
        //fmt.Printf("--- querying for sub object keyed by %v\n", key)
        if queryObj[key] != nil {
            queryObj = queryObj[key].(map[interface{}]interface{})
            //fmt.Printf("--- Sub object keyed by %v :\n%v\n\n", key, queryObj)
        } else {
            //fmt.Printf("--- No sub object keyed by %v :\n%v\n\n", key)
            break
        }
    }
    if queryObj != nil {
        lastKey := keys[len(keys)-1]
        //fmt.Printf("--- querying for value keyed by %v\n", lastKey)

        if queryObj[lastKey] != nil {
            objType := reflect.TypeOf(queryObj[lastKey])
            //fmt.Printf("Type of value %v\n", objType)
            if objType.String() == "[]interface {}" {
                //fmt.Printf("Object is a array %v\n", objType)
                tempArr := queryObj[lastKey].([]interface{})
                //fmt.Printf("Length of array is %v\n", len(tempArr))
                if indexOfElementInArray >= 0 && indexOfElementInArray < len(tempArr) {
                    value = queryObj[lastKey].([]interface{})[indexOfElementInArray].(string)
                }
            } else {
                value = queryObj[lastKey].(string)
            }
        }
    }

    return value
}

答案 1 :(得分:1)

我认为你有一个额外的嵌套水平。可能不需要Config结构。你能尝试以下定义:

type Ecs struct {
    Services []Service
}

type Service struct {
    Name           string
    TaskDefinition string
    DesiredCount   int
}

然后尝试解组yaml数据。然后,您可以将数据作为ecs.Services访问。

答案 2 :(得分:0)

You should use struct tags since you name the fields with lower case letters. This applies to different named fields as well.

See the example for how to fix this: https://play.golang.org/p/WMmlQsqYeB the other answer is incorrect.