Viper: Add support for default values in struct

Created on 4 Jan 2017  路  4Comments  路  Source: spf13/viper

Loading flags with defaults in struct:

type Config struct {
    Host string
}
cfg := &Config{
    Host: "example.com",
}
if b,err := yaml.Marshal(cfg); err != nil {
    log.Panicf("cannot marshal defaults?! %s", err)
} else {
    viper.SetConfigType("yaml")
    r := bytes.NewReader(b)
    viper.ReadConfig(r)
}
viper.AddConfigPath(".")
if err := viper.ReadInConfig(); err != nil {
    log.Panicf("cannot read: %s", err)
}
cfg = &Config{}
if err := viper.Unmarshal(cfg); err != nil {
    log.Panicf("cannot unmarshal config: %s", err)
}
spew.Dump(cfg)

config.yaml

 addr: localhost:8080

output:

 (*main.Config)(0xc4200d3de0)({
   Host: (string) (len=11) "example.com",
   Addr: (string) (len=14) "localhost:8080"
 })

Most helpful comment

@orian, it actually doesn't modify values already in the struct either.

cfg := &Config{
    Host: "example.com",
}
viper.AddConfigPath(".")
if err := viper.ReadInConfig(); err != nil {
    logrus.Fatalf("cannot read: %s", err)
}
if err := viper.Unmarshal(cfg); err != nil {
    logrus.Fatalf("cannot unmarshal config: %s", err)
}
fmt.Println(cfg.Host) // will print "example.com" instead of the value in the config file.
host: localhost:8080

All 4 comments

The solution is trivial, pass the initialized values to Unmarshal, it won't modify the values not present in config file:

cfg := &Config{
    Host: "example.com",
}
viper.AddConfigPath(".")
if err := viper.ReadInConfig(); err != nil {
    logrus.Fatalf("cannot read: %s", err)
}
if err := viper.Unmarshal(cfg); err != nil {
    logrus.Fatalf("cannot unmarshal config: %s", err)
}
spew.Dump(cfg)

Please close the issue. Maybe add a note to FAQ?

@orian, it actually doesn't modify values already in the struct either.

cfg := &Config{
    Host: "example.com",
}
viper.AddConfigPath(".")
if err := viper.ReadInConfig(); err != nil {
    logrus.Fatalf("cannot read: %s", err)
}
if err := viper.Unmarshal(cfg); err != nil {
    logrus.Fatalf("cannot unmarshal config: %s", err)
}
fmt.Println(cfg.Host) // will print "example.com" instead of the value in the config file.
host: localhost:8080

This must have been fixed along the way or was working as expected:

type Config struct {
    Host string
    Url string
}

func TestDefaults(t *testing.T) {
    cfg := &Config{
        Host: "default",
        Url: "default",
    }

    vv := viper.New()
    vv.SetConfigType("yaml")
    err := vv.ReadConfig(bytes.NewReader([]byte(`Host: "blue" `)))
    if err != nil {
        t.Error("Failed to read config")
    }

    vv.Unmarshal(&cfg)

    fmt.Printf("%+v\n", cfg)
}
&{Host:blue Url:default}

Default values provided by viper.SetDefault are being overrided as well, if only server.database is specified in config file then server.timeout = 0

viper.SetDefault("server.database", DefaultDatabase)
viper.SetDefault("server.timeout", 30*time.Second)
Was this page helpful?
0 / 5 - 0 ratings