Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Binding flags and unmarshalling to nested structures #190

Closed
benoitmasson opened this issue May 25, 2016 · 7 comments
Closed

Binding flags and unmarshalling to nested structures #190

benoitmasson opened this issue May 25, 2016 · 7 comments

Comments

@benoitmasson
Copy link
Contributor

Hi,

I've run into a small issue when overriding a configuration value with a command-line flag: after unmarshalling to a struct, fields in nested sub-structures are not updated, while level-1 fields are.

For example, this piece of code:

type Config struct {
    Test1  int
    Nested struct {
        Test2 int
    }
}

func main() {
    var cfg Config

    // init flags and bind to viper
    flag.Int("test1", 0, "Test1")
    flag.Int("nested.test2", 0, "Nested test2")
    flag.Parse()
    viper.BindPFlag("test1", flag.Lookup("test1"))
    viper.BindPFlag("nested.test2", flag.Lookup("nested.test2"))

    // read config
    viper.SetConfigName("config")
    viper.AddConfigPath(".")
    err := viper.ReadInConfig()
    if err != nil {
        panic(err)
    }
    err = viper.Unmarshal(&cfg)
    if err != nil {
        panic(err)
    }

    // run
    fmt.Println(viper.GetInt("test1"), viper.GetInt("nested.test2"))
    fmt.Println(cfg.Test1, cfg.Nested.Test2)
}

associated with this configuration file:

test1 = 11

[nested]
test2 = 12

produces the following output:

> ./main
11 12
11 12
> ./main --test1=21 --nested.test2=22
21 22
21 12

The last nested.test2 value is correct when using viper.GetInt("nested.test2"), but it is not in the struct, while both values are correct for test1.

This may be related to Issue #168, where it also appears that the nested struct is not updated when the internal map changes.

@benoitmasson
Copy link
Contributor Author

PR #195 should have fixed this issue.

@sm3142
Copy link

sm3142 commented Aug 4, 2016

I'm not sure what is actually happening is what you hope for. If you dump out the resulting config with AllSettings(), you see that the binding has actually created an extra top level item:

map[nested:map[test2:12] test1:21 nested.test2:22]

viper.GetInt("nested.test2") will actually fetch the top level item, not the nested one. The last example in Accessing Nested Keys of the Viper documentation explains why this is so:

Lastly, if there exists a key that matches the delimited key path, its value will be returned instead.

PS: Yep, I just spent some ungodly amount of time trying to bind nested objects too. Apparently it is not supported.

edit: Just saw that PR #195 hasn't been merged to master yet. So there is still hope :)

@benoitmasson
Copy link
Contributor Author

Indeed, the current version of AllSettings() does not consider flag names with dot separator as “nested” keys.

That's the purpose of commit d8a62ff in PR #195, it fixed the issue for me. Feel free to use the modified code until it is merged.

@sm3142
Copy link

sm3142 commented Aug 15, 2016

@benoitmasson: Actually did pull your patch, works beautifully for me as well. Thanks a bundle!

awfm9 pushed a commit that referenced this issue Oct 8, 2016
Fixes #71, #93, #158, #168, #209, #141, #160, #162, #190

* Fixed: indentation in comment
* Fixed: Get() returns nil when nested element not found
* Fixed: insensitiviseMaps() made recursive so that nested keys are lowercased
* Fixed: order of expected<=>actual in assert.Equal() statements
* Fixed: find() looks into "overrides" first
* Fixed: TestBindPFlags() to use a new Viper instance
* Fixed: removed extra aliases from display in Debug()
* Added: test for checking precedence of dot-containing keys.
* Fixed: Set() and SetDefault() insert nested values
* Added: tests for overriding nested values
* Changed: AllKeys() includes all keys / AllSettings() includes overridden nested values
* Added: test for shadowed nested key
* Fixed: properties parsing generates nested maps
* Fixed: Get() and IsSet() work correctly on nested values
* Changed: modifier README.md to reflect changes
@awfm9
Copy link

awfm9 commented Oct 8, 2016

Fixed by #195

@awfm9 awfm9 closed this as completed Oct 8, 2016
@sjames-iberis
Copy link

Is there still a similar issue with use of UnmarhsalKey?
For example, if I add the following to the original code above:

err = viper.UnmarhsalKey("nested", &cfg.Nested)
fmt.Println(cfg.Nested.Test2)

It always produces the value from the config file; the command line override never takes effect.

./main --test1=21 --nested.test2=22
21 22
21 22
12

@tunhuit
Copy link

tunhuit commented Jun 17, 2021

I have the same issue. The command line don't override the value. The struct always use config file value instead of command line.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants