From dcd7fbbff759c1a4465ec97971ab48b100af7b98 Mon Sep 17 00:00:00 2001 From: Bogdan Drutu Date: Fri, 18 Sep 2020 10:08:05 -0700 Subject: [PATCH] Allow nil values to be returned by AllSettings Signed-off-by: Bogdan Drutu --- viper.go | 14 +++++++++++--- viper_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/viper.go b/viper.go index c3130c2..4d5b02f 100644 --- a/viper.go +++ b/viper.go @@ -199,6 +199,7 @@ type Viper struct { automaticEnvApplied bool envKeyReplacer StringReplacer allowEmptyEnv bool + allowNilValues bool config map[string]interface{} override map[string]interface{} @@ -257,6 +258,14 @@ func KeyDelimiter(d string) Option { }) } +// AllowNilValues tells Viper to not ignore keys with nil values. +// For backward compatibility reasons this is false by default. +func AllowNilValues(allowNilValues bool) Option { + return optionFunc(func(v *Viper) { + v.allowNilValues = allowNilValues + }) +} + // StringReplacer applies a set of replacements to a string. type StringReplacer interface { // Replace returns a copy of s with all replacements performed. @@ -1960,9 +1969,8 @@ func (v *Viper) AllSettings() map[string]interface{} { // start from the list of keys, and construct the map one value at a time for _, k := range v.AllKeys() { value := v.Get(k) - if value == nil { - // should not happen, since AllKeys() returns only keys holding a value, - // check just in case anything changes + // Default behavior is to not allow nil values, but users can enable this via config. + if value == nil && !v.allowNilValues { continue } path := strings.Split(k, v.keyDelim) diff --git a/viper_test.go b/viper_test.go index 09d5021..66fe457 100644 --- a/viper_test.go +++ b/viper_test.go @@ -2258,6 +2258,37 @@ func TestKeyDelimiter(t *testing.T) { assert.Equal(t, expected, actual) } +var yamlExampleWithNilValues = []byte(`Hacker: true +name: steve +hobbies: +clothing: + jacket: +`) + +func TestNilValues(t *testing.T) { + vNil := NewWithOptions(AllowNilValues(true)) + vNil.SetConfigType("yaml") + require.NoError(t, vNil.ReadConfig(strings.NewReader(string(yamlExampleWithNilValues)))) + expectedNil := map[string]interface{}{ + "hacker": true, + "name": "steve", + "hobbies": nil, + "clothing": map[string]interface{}{ + "jacket": nil, + }, + } + assert.EqualValues(t, expectedNil, vNil.AllSettings()) + + vNotNil := NewWithOptions(AllowNilValues(false)) + vNotNil.SetConfigType("yaml") + require.NoError(t, vNotNil.ReadConfig(strings.NewReader(string(yamlExampleWithNilValues)))) + expectedNotNil := map[string]interface{}{ + "hacker": true, + "name": "steve", + } + assert.EqualValues(t, expectedNotNil, vNotNil.AllSettings()) +} + func BenchmarkGetBool(b *testing.B) { key := "BenchmarkGetBool" v = New()