To support yaml and json config files, it would be nice if it is possible to set default values for nested key.
Currently when I set a default value for.
viper.SetDefault("datastore.metric.host", "0.0.0.0")
The key would not be interpreted as a nested key and it is not possible to overwrite it with a yaml or json config file
Same thing for toml config files
I have the same experience. +1
workaround for now:
if viper.Get("cluster.enabled") == nil {
viper.SetDefault("cluster.enabled", false)
}
An implementation to set default values for nested keys would would break the current api of viper.
So it would be nice @spf13 would comment on this issue if viper should support this in feature.
Then someone can work on this and contribute to viper.
I'm also facing this issue.
@jacobstr I think that merge in your fork accidentally closed this issue.
@LStuker mind re-opening it please?
I don't have the permission to re-open the issue because I didn't closed it by my self. Only maintainer can do this. @jacobstr and @spf13 can you please re-open the issue and give an comment on this issue.
Sorry guys! No idea why my merge closed this issue!
I was trying to figure it out myself; educated guess is that the phrase resolved spf13#71 in the linked PR's description is the culprit.
I think triggering ticket closure from a fork is utterly bananas, but that's github's fault not anyone here :)
I am running into the exact same issue when trying to set up default values for nested keys. These values are being treated like top level keys.
@spf13 - Will this be supported by Viper?
I think this should be supported by Viper. There's a clear need for it as indicated by this thread. There are definitely some challenges in implementing it, but I believe that the addition would be worthwhile.
For me the big question is expressed by the following scenario...
The app has set a default
viper.SetDefault("datastore.metric.host", "0.0.0.0")
In a config file I provide a value like
datastore.metric.port = 80
or
datastore.metric = map{ port:80 }
In these two scenarios what should happen or more importantly what was the users intent?
If we can come up with a sane way to handle these scenarios, then I'm all for it.
@spf13 - in the scenario you described, I would expect some action to happen on 0.0.0.0:80. The documentation states that Viper uses a strict precedence order, so it's a bit surprising that nested values aren't subject to the same rules.
Merging nested data structures while respecting precedence levels in mind w/ a feature toggle (that defaults to off?) would be perfect IMO.
I think I may have run into this issue as well. For now, a workaround I am performing is by using MergeConfig with a string reader seems to get me up and running with nested default values:
defaults := `
[foo]
[foo.bar]
baz = 1
`
config.MergeConfig(strings.NewReader(defaults))
config.MergeConfig(realConfigFile)
Forgive me. I've misunderstood this thread completely. The default should work completely as you've outlined. I've been reading this as setting things at nested values in any layer.
At all layers except for the default one those two lines are actually two very different operations. I'll comment on that challenge.
metric.port = 80 is updating a nested value. I would expect in this case that any existing values (other than port) under datastore.metric would remain
metric = map{ port:80 } is replacing a value. I would expect in this case that any existing values in metric would go way as I'm setting the entire value of metric to this new map. I wouldn't expect this to merge.
This is the big challenge here. I don't think a global setting is the solution because it's reasonable to expect that a user would want to use both operations (update and replace). We dealt with the same thing in MongoDB (https://docs.mongodb.org/manual/reference/method/db.collection.update/#update-specific-fields).
We need to figure out a way to handle this across all of the different layers and formats in a sane way.
I think the right solution is to follow the pattern above with replace and update operations handled as outlined there. I think it will be complicated to get it right with all the layers, but should work consistently and enables end users to replace entire parent values or update a key without touching siblings.
Does any of this make sense?
+1
SGTM :+1:
@spf13 That would be the best of both worlds :+1:
I've tried to do the workaround from here #71 (comment)
I made it work with this:
if c.Get("params.p3") == nil {
c.Set("params.p3", "333")
}
Seems like there's consensus around a fix for this issue. Anything holding back implementation other than time?
I believe my PR (#195) solves this issue. Feel free to try the code and to send me feedback!
Most helpful comment
Forgive me. I've misunderstood this thread completely. The default should work completely as you've outlined. I've been reading this as setting things at nested values in any layer.
At all layers except for the default one those two lines are actually two very different operations. I'll comment on that challenge.
metric.port = 80is updating a nested value. I would expect in this case that any existing values (other than port) underdatastore.metricwould remainmetric = map{ port:80 }is replacing a value. I would expect in this case that any existing values in metric would go way as I'm setting the entire value of metric to this new map. I wouldn't expect this to merge.This is the big challenge here. I don't think a global setting is the solution because it's reasonable to expect that a user would want to use both operations (update and replace). We dealt with the same thing in MongoDB (https://docs.mongodb.org/manual/reference/method/db.collection.update/#update-specific-fields).
We need to figure out a way to handle this across all of the different layers and formats in a sane way.
I think the right solution is to follow the pattern above with replace and update operations handled as outlined there. I think it will be complicated to get it right with all the layers, but should work consistently and enables end users to replace entire parent values or update a key without touching siblings.
Does any of this make sense?