【问题标题】:Can't convert []interface{} to []string in Go无法在 Go 中将 []interface{} 转换为 []string
【发布时间】:2022-01-06 17:22:03
【问题描述】:

XY 问题: 我正在尝试读入如下的 YAML 文件,并输出一组元组,这些元组组合了 YAML 文件中的某些键和值。例如。鉴于此 YAML 数据:

---
fruit:
    apple:
        colour:
            - green
    'banana':
        colour:
            - yellow
    pear:
        colour:
            - green
            - yellow

我想将“fruit”下的每个键与“color”下的每个值组合成元组。我的元组看起来像这样:

apple:green
banana:yellow
pear:green
pear:yellow

为此,我使用 map[string]interface{} 将我的 YAML 数据解组到 Go - 我不能使用结构,因为“fruit”下面的键名可以是任何东西,所以我需要使用动态类型。到目前为止,这是我的代码:

package main

import (
    "fmt"
    "log"

    "gopkg.in/yaml.v3"
)

var data string = `
---
fruit:
    apple:
        colour:
            - green
    'banana':
        colour:
            - yellow
    pear:
        colour:
            - green
            - yellow
`

func main() {
    m := make(map[string]interface{})
    err := yaml.Unmarshal([]byte(data), &m)
    if err != nil {
        log.Fatal("Failed to parse YAML file")
    }

    for _, v := range m {
        fruits := v.(map[string]interface{})
        for fruit, v2 := range fruits {
            colours := v2.(map[string]interface{})["colour"]
            for colour := range colours {
                fmt.Println("%v:%v\n", fruit, colour)
            }
        }
    }
}

游乐场链接:https://go.dev/play/p/v8iuzmMLtjX

问题是for colour := range colours - 我收到错误:

cannot range over colours (type interface {})

我找到了this answer to a similar question,它说我不能直接将 []interface{} 转换为 [] 字符串,而是必须遍历这些值。这就是我在这里尝试做的。 v2 变量的结果是 map[string]interface {} 类型,例如可以是 map[colour:[green yellow]]。然后我尝试将其转换为另一个 map[string]interface{} 以获取“color”的值,该值适用于 []interface{} 类型 [green yellow] 并存储在 colours 变量中。

但由于某种原因,我无法遍历 colours。我不明白我的解决方案和icza's solution in the linked answer 有什么不同(我已经链接了他们的 Playground 链接)。我的解决方案中colours 的数据类型是[]interface{},而在icza 的解决方案中t 的数据类型也是[]interface{} - 但在这种情况下,可以遍历切片并访问里面的值。

我尝试的另一个解决方案是来自this answer to a different question,即尝试将[]interface{}直接转换为[]字符串:

c := colours.([]string)

那也没用:

panic: interface conversion: interface {} is []interface {}, not []string

我需要做什么才能使这个解决方案发挥作用?

【问题讨论】:

  • 您可以比map[string]interface{} 更具体:go.dev/play/p/Le00DlzOz8T。这应该使建立您的价值观变得微不足道。
  • @CeriseLimón 这可以帮助我遍历颜色 - 但我无法断言字符串; for colour := range c { cs := colour.(string) } --> invalid type assertion: colour.(string) (non-interface type int on left)。仍在尝试找出为什么它认为我正在转换为 int。
  • @Peter 当键是动态/任意命名并且没有从有关该主题的其他问题中找到任何智慧时,我在尝试解组到结构时遇到了问题。你知道我将如何使用你建议的结构来解组我的 YAML 数据吗?
  • 啊,宾果游戏!这已经为我解决了,谢谢@CeriseLimón :))

标签: go interface type-conversion yaml unmarshalling


【解决方案1】:

由于在编译时只有映射的键是未知的,但结构已知的,因此您可以比 map[string]interface{} 更具体:

type Document struct {
    Fruits map[string]Fruit `yaml:"fruit"`
}

type Fruit struct {
    Colours []string `yaml:"colour"`
}

这使得建立你的价值观变得微不足道:

package main

import (
    "fmt"

    "gopkg.in/yaml.v3"
)


var data string = `
---
fruit:
    apple:
        colour:
            - green
    'banana':
        colour:
            - yellow
    pear:
        colour:
            - green
            - yellow
`

func main() {
    var m Document
    yaml.Unmarshal([]byte(data), &m)

    for name, fruit := range m.Fruits {
        for _, colour := range fruit.Colours {
            fmt.Printf("%s:%s\n", name, colour)
        }
    }
}

在操场上试试看:https://go.dev/play/p/phnvRriQmMh

【讨论】:

  • 太好了,谢谢!因此,在 Cerise 的帮助下,我修复了我原来的解决方案,在你的帮助下,我得到了一个不同但也有效的解决方案。下一个工作日,我将尝试将其应用到我的真实用例中,看看哪个效果更好。不管怎样,感谢您的帮助!
【解决方案2】:

感谢 Cerise Limón 提供的更正帮助我解决了我的解决方案!

package main

import (
    "fmt"
    "log"

    "gopkg.in/yaml.v3"
)

var data string = `
---
fruit:
    apple:
        colour:
            - green
    'banana':
        colour:
            - yellow
    pear:
        colour:
            - green
            - yellow
`

func main() {
    m := make(map[string]interface{})
    err := yaml.Unmarshal([]byte(data), &m)
    if err != nil {
        log.Fatal("Failed to parse YAML file")
    }

    for _, v := range m {
        fruits := v.(map[string]interface{})
        for fruit, v2 := range fruits {
            colours := v2.(map[string]interface{})["colour"]
            c := colours.([]interface{})
            for _, colour := range c {
                cs := colour.(string)
                fmt.Printf("%v:%v\n", fruit, cs)
            }
        }
    }
}

https://go.dev/play/p/dZT84tDLMjE

【讨论】:

    猜你喜欢
    • 2015-01-14
    • 1970-01-01
    • 2017-10-17
    • 2016-09-18
    • 2014-09-25
    • 2020-07-17
    • 2021-05-26
    • 2021-05-11
    • 2017-01-01
    相关资源
    最近更新 更多