D.Sync
D.Sync

Reputation: 77

Problem accessing nested YAML structure in Viper

Recently trying out Viper with my Cobra app to parse my config, but turned out that I couldn't parse the inner nested block. The map for ClustersOuter is always empty, and I am sure the mapstructure is tagged correctly. I would prefer to use Viper Unmarshall feature rather than Getting the data type manually.

Output

map[zones:[{ClustersOuter:map[] Id:1} {ClustersOuter:map[] Id:2}]]

Somehow it seems like I can only retrieve the zone map and its ID, but not any map for the clusters map.

My inputs: config.yml

zones:
  - id: 1
    clusters:
      - cluster_id: 1
        k3s_config_file: "k3s-cluster-1-config.yaml"
      - cluster_id: 2
        k3s_config_file: "k3s-cluster-2-config.yaml"
      - cluster_id: 3
        k3s_config_file: "k3s-cluster-3-config.yaml" 
  - id: 2
    clusters:
      - cluster_id: 1
        k3s_config_file: "k3s-cluster-1-config.yaml"
      - cluster_id: 2
        k3s_config_file: "k3s-cluster-2-config.yaml"
      - cluster_id: 3
        k3s_config_file: "k3s-cluster-3-config.yaml"   

configuration.go

package config

type Zones struct {
    ClustersOuter map[string][]ClustersOuter `mapstructure:"zones"`
    Id            int                        `mapstructure:"id"`
}
type ClustersInner struct {
    ClusterId     int    `mapstructure:"cluster_id"`
    K3sConfigFile string `mapstructure:"k3s_config_file"`
}

type ClustersOuter struct {
    ClustersInner map[string][]ClustersInner `mapstructure:"clusters"`
}

type ConfigSuper map[string][]Zones

main.go

    viper.SetConfigName("config")
    viper.AddConfigPath(".")
    viper.SetConfigType("yaml")

    if err := viper.ReadInConfig(); err != nil {
        log.Fatalf("Error reading config file, %s", err)
        return false
    }

    var configuration config.ConfigSuper
    err := viper.Unmarshal(&configuration)
    if err != nil {
        log.Fatalf("unable to decode into struct, %v", err)
    }

    fmt.Printf("%+v\n", configuration)

Upvotes: 0

Views: 2796

Answers (1)

Clark McCauley
Clark McCauley

Reputation: 1457

Your struct types do not reflect the YAML type, it looks like you've combined maps and slices together when you're only dealing with slices. In YAML, maps look like this:

myMap:
    key1: value1

and arrays look like this:

myArray:
    - foo: bar

Unfortunately, your config object is messed up enough that I can't understand the intent so I re-wrote it for you. These types can be decoded by Viper without any issues.

type Config struct {
    Zones []Zone `mapstructure:"zones"`
}

type Zone struct {
    ID       int        `mapstructure:"id"`
    Clusters []Cluster `mapstructure:"clusters"`
}

type Cluster struct {
    ClusterID     int    `mapstructure:"cluster_id"`
    K3sConfigFile string `mapstructure:"k3s_config_file"`
}

The Config struct is the root object, make sure to unmarshal into that.

config := Config{}
err = viper.Unmarshal(&config)

Upvotes: 3

Related Questions