user16461680
user16461680

Reputation: 35

How to iterate over yml sections with structs?

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: [email protected]
    skype:
      type: skype
      value: skypeacc

Can I get structure items like this? I could not get contact data like that. Is it possible?

Upvotes: 0

Views: 573

Answers (2)

larsks
larsks

Reputation: 311720

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: [email protected]
    - 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 :  [email protected]
   skype :  skypeacc

Upvotes: 0

koioannis
koioannis

Reputation: 191

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: [email protected]
      - type: skype
        value: skypeacc
      - type: email
        value: [email protected]
  • 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"`
}

Upvotes: 1

Related Questions