From 7267c511e3d6be0f9acdfa466f27739bf6cbae1d Mon Sep 17 00:00:00 2001 From: Fredrik Allansson Date: Wed, 29 Jan 2025 16:45:26 +0100 Subject: [PATCH] Add config flag for merging into existing map values instead of creating new ones. --- mapstructure.go | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/mapstructure.go b/mapstructure.go index e77e63ba..21553e88 100644 --- a/mapstructure.go +++ b/mapstructure.go @@ -282,6 +282,10 @@ type DecoderConfig struct { // DecodeNil, if set to true, will cause the DecodeHook (if present) to run // even if the input is nil. This can be used to provide default values. DecodeNil bool + + // MergeIntoExisting, if set to true, will merge nested maps or struct with + // any already exsting structs or maps in the output. + MergeIntoExisting bool } // A Decoder takes a raw interface value and turns it into structured @@ -1058,12 +1062,31 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re mType := reflect.MapOf(vKeyType, vElemType) vMap := reflect.MakeMap(mType) - // Creating a pointer to a map so that other methods can completely - // overwrite the map if need be (looking at you decodeMapFromMap). The - // indirection allows the underlying map to be settable (CanSet() == true) - // where as reflect.MakeMap returns an unsettable map. - addrVal := reflect.New(vMap.Type()) - reflect.Indirect(addrVal).Set(vMap) + var addrVal reflect.Value + if d.config.MergeIntoExisting { + // Check if the map already contains a value for this field. + addrVal = valMap.MapIndex(reflect.ValueOf(keyName)) + if addrVal.IsValid() { + // The map already contains a value. Pull it out into a modifiable value. + newAddrVal := reflect.New(addrVal.Type()) + reflect.Indirect(newAddrVal).Set(addrVal) + addrVal = newAddrVal + } else { + // Creating a pointer to a map so that other methods can completely + // overwrite the map if need be (looking at you decodeMapFromMap). The + // indirection allows the underlying map to be settable (CanSet() == true) + // where as reflect.MakeMap returns an unsettable map. + addrVal = reflect.New(vMap.Type()) + reflect.Indirect(addrVal).Set(vMap) + } + } else { + // Creating a pointer to a map so that other methods can completely + // overwrite the map if need be (looking at you decodeMapFromMap). The + // indirection allows the underlying map to be settable (CanSet() == true) + // where as reflect.MakeMap returns an unsettable map. + addrVal = reflect.New(vMap.Type()) + reflect.Indirect(addrVal).Set(vMap) + } err := d.decode(keyName, x.Interface(), reflect.Indirect(addrVal)) if err != nil {