英文:
How to iterate over yml sections with structs?
问题
我使用viper。我正在尝试从带有yml-config的结构体中获取信息。
type Config struct {
Account User `mapstructure:"user"`
}
type User struct {
Name string `mapstructure:"name"`
Contacts []Contact `mapstructure:"contact"`
}
type Contact struct {
Type string `mapstructure:"type"`
Value string `mapstructure:"value"`
}
func Init() *Config {
conf := new(Config)
viper.SetConfigType("yaml")
viper.ReadInConfig()
...
viper.Unmarshal(conf)
return conf
}
...
config := Init()
...
for _, contact := range config.Account.Contacts {
type := contact.Type
value := contact.Value
}
config.yml文件如下:
user:
name: John
contacts:
email:
type: email
value: test@test.com
skype:
type: skype
value: skypeacc
你能像这样获取结构体的项吗?我无法像那样获取联系人数据。这种情况可能吗?
英文:
I use viper. I'm trying to get info from structs with yml-config.
type Config struct {
Account User `mapstructure:"user"`
}
type User struct {
Name string `mapstructure:"name"`
Contacts []Contact `mapstructure:"contact"`
}
type Contact struct {
Type string `mapstructure:"type"`
Value string `mapstructure:"value"`
}
func Init() *Config {
conf := new(Config)
viper.SetConfigType("yaml")
viper.ReadInConfig()
...
viper.Unmarshal(conf)
return conf
}
...
config := Init()
...
for _, contact := range config.Account.Contacts {
type := contact.type
vlaue := contact.value
}
And config.yml
user:
name: John
contacts:
email:
type: email
value: test@test.com
skype:
type: skype
value: skypeacc
Can I get structure items like this? I could not get contact data like that. Is it possible?
答案1
得分: 1
如果我理解你的目标正确,并且根据你提供的for
循环,你实际上需要一个YAML的序列,也就是一个数组。所以你的最终YAML文件应该如下所示:
user:
name: John
contacts:
- type: email
value: test@test.com
- type: skype
value: skypeacc
- type: email
value: joe@example.com
此外,你在Contacts
切片的标签中有一个拼写错误。它应该与YAML键匹配:
type User struct {
Name string `mapstructure:"name"`
Contacts []Contact `mapstructure:"contacts"`
}
如果你希望保留原始的YAML文件结构,你必须为每个YAML键提供一个标签(和相应的结构字段),这样就无法直接循环遍历它,因为email
和skype
会被解析为结构字段。原始YAML文件的结构示例如下:
type Config struct {
Account User `mapstructure:"user"`
}
type User struct {
Name string `mapstructure:"name"`
Contacts Contacts `mapstructure:"contacts"`
}
type Contacts struct {
Email Contact `mapstructure:"email"`
Skype Contact `mapstructure:"skype"`
}
type Contact struct {
Type string `mapstructure:"type"`
Value string `mapstructure:"value"`
}
英文:
If I'm getting right what you want to achieve, and based on the for
loop you provided;
- What you actually need is a YAML sequence, which is an array. So your final YAML file should look like;
user:
name: John
contacts:
- type: email
value: test@test.com
- type: skype
value: skypeacc
- type: email
value: joe@example.com
- Also, you have a typo in your tag on
Contacts
slice. It should match the YAML key;
type User struct {
Name string `mapstructure:"name"`
Contacts []Contact `mapstructure:"contacts"`
}
If you wish to keep the original YAML file structure you have to provide a tag (and the corresponding struct field) for each YAML key, so looping over it wouldn't be possible out of the box, because email
and skype
are parsed as struct fields. An example of the struct for the original YAML file would be;
type Config struct {
Account User `mapstructure:"user"`
}
type User struct {
Name string `mapstructure:"name"`
Contacts Contacts `mapstructure:"contacts"`
}
type Contacts struct {
Email Contact `mapstructure:"email"`
Skype Contact `mapstructure:"skype"`
}
type Contact struct {
Type string `mapstructure:"type"`
Value string `mapstructure:"value"`
}
答案2
得分: 0
我认为唯一的重要问题是,在你的数据结构中,你将Contacts
声明为一个列表,但在你的YAML文件中,它是一个字典。如果你按照以下方式组织你的输入文件:
user:
name: John
contacts:
- type: email
value: test@test.com
- type: skype
value: skypeacc
那么你可以这样读取它:
package main
import (
"fmt"
"github.com/spf13/viper"
)
type Config struct {
User User
}
type User struct {
Name string
Contacts []Contact
}
type Contact struct {
Type string
Value string
}
func main() {
var cfg Config
viper.SetConfigName("config")
viper.AddConfigPath(".")
err := viper.ReadInConfig()
if err != nil {
panic(err)
}
viper.Unmarshal(&cfg)
fmt.Println("user: ", cfg.User.Name)
for _, contact := range cfg.User.Contacts {
fmt.Println(" ", contact.Type, ": ", contact.Value)
}
}
上面的代码可以直接运行;你只需要将它放入一个文件中并构建它。当我运行上面的示例时,我得到的输出是:
user: John
email : test@test.com
skype : skypeacc
英文:
I think the only significant problem is that in your data structures you've declared Contacts
as a list, but in your YAML file it's a dictionary. If you structure your input file like this:
user:
name: John
contacts:
- type: email
value: test@test.com
- type: skype
value: skypeacc
Then you can read it like this:
package main
import (
"fmt"
"github.com/spf13/viper"
)
type Config struct {
User User
}
type User struct {
Name string
Contacts []Contact
}
type Contact struct {
Type string
Value string
}
func main() {
var cfg Config
viper.SetConfigName("config")
viper.AddConfigPath(".")
err := viper.ReadInConfig()
if err != nil {
panic(err)
}
viper.Unmarshal(&cfg)
fmt.Println("user: ", cfg.User.Name)
for _, contact := range cfg.User.Contacts {
fmt.Println(" ", contact.Type, ": ", contact.Value)
}
}
The above code is runnable as-is; you should just be able to drop it
into a file and build it. When I run the above example, I get as
output:
user: John
email : test@test.com
skype : skypeacc
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论