首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Golang嵌套Yaml值

Golang嵌套Yaml值
EN

Stack Overflow用户
提问于 2016-07-11 15:35:14
回答 3查看 14.6K关注 0票数 6

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

任何关于如何处理这个问题的建议都是值得欢迎的,因为我已经遇到了几个Yaml库,但它们中的任何一个都无法充分发挥作用。

test.yaml:

代码语言:javascript
复制
ecs:
  services:
    - name: my-service
      taskDefinition: my-task-def
      desiredCount: 1

Yaml.go

代码语言:javascript
复制
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)
    }
}

输出

代码语言:javascript
复制
$ 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}}
EN

回答 3

Stack Overflow用户

发布于 2017-11-09 09:47:23

我有一个类似的要求,在yaml文件上,我需要执行嵌套检索。由于我发现没有开箱即用的解决方案,所以我不得不自己写。

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

代码语言:javascript
复制
"a": "Easy!"
"b":
  "c": "2"
  "d": ["3", "4"]
"e":
  "f": {"g":"hi","h":"6"}

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

代码语言:javascript
复制
--- 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中最通用的结构是接口{}。yaml最适合的结构是map[interface{}]interface{}。对于来自java的人来说,这类似于Map<Object,Object>。一旦数据被解封,我必须编写一个函数,它可以使用嵌套键遍历结构并返回值。

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

代码语言:javascript
复制
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
}
票数 4
EN

Stack Overflow用户

发布于 2016-07-11 19:28:32

您应该使用struct标记,因为您用小写字母命名字段。这也适用于不同的命名字段。

请参阅如何解决此问题的示例:https://play.golang.org/p/WMmlQsqYeB其他答案是不正确的。

票数 2
EN

Stack Overflow用户

发布于 2016-07-11 15:43:17

我觉得你有额外的筑巢能力。可能不需要Config结构。您能否尝试以下定义:

代码语言:javascript
复制
type Ecs struct {
    Services []Service
}

type Service struct {
    Name           string
    TaskDefinition string
    DesiredCount   int
}

然后尝试解压缩yaml数据。然后,您可以以ecs.Services的形式访问数据。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38310894

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档