Skip to content

fix: recurse into []*struct with Deep struct-to-map decoding (#196) - #199

Open
chiliec wants to merge 1 commit into
go-viper:mainfrom
chiliec:fix/deep-map-slice-of-struct-pointers
Open

fix: recurse into []*struct with Deep struct-to-map decoding (#196)#199
chiliec wants to merge 1 commit into
go-viper:mainfrom
chiliec:fix/deep-map-slice-of-struct-pointers

Conversation

@chiliec

@chiliec chiliec commented Aug 23, 2026

Copy link
Copy Markdown

Fixes #196.

Problem

With DecoderConfig.Deep (or a ,deep tag), struct-to-map decoding recurses into []MyStruct and produces []map[string]any, but a []*MyStruct field is left as raw []*MyStruct in the output map. The two slice shapes decode inconsistently.

Root cause is in decodeMapFromStruct's reflect.Slice branch, which only recurses when the element Kind is reflect.Struct:

switch v.Type().Elem().Kind() {
case reflect.Struct:
    childType = reflect.TypeOf(map[string]any{})
default:
    childType = v.Type().Elem()   // []*T lands here → copied verbatim
}

For []*T the element Kind is reflect.Ptr, so it falls into default and the pointer elements are copied as-is.

Fix

Treat a slice whose element is a pointer-to-struct the same as a slice of structs, mapping both to []map[string]any. The existing d.decode call already dereferences the pointer elements, so no other change is needed.

Test

Extended the existing TestDecode_structArrayDeepMap with a ChildrenC []*SourceChild field alongside the current []SourceChild and *[]SourceChild cases. It fails before the change (children-c comes back as []*mapstructure.SourceChild) and passes after ([]map[string]any).

Verification (real runs, Go 1.24.6)

  • RED: with the test but without the mapstructure.go change → FAIL: TestDecode_structArrayDeepMap (children-c = []*mapstructure.SourceChild{...}).
  • GREEN: with the fix → PASS.
  • Full suite as CI runs it: go test -race -shuffle=on ./...ok github.com/go-viper/mapstructure/v2.
  • gofmt -s, gofumpt, and go vet ./... all clean.

AI assistance disclosure

Prepared with an AI coding assistant; the root-cause analysis, fix, extended test, and the RED→GREEN + race/lint verification above were run against a real Go 1.24.6 toolchain, not generated.

decodeMapFromStruct only mapped slices whose element Kind is reflect.Struct
into []map[string]any under Deep. A slice of pointers to structs ([]*T) has
element Kind reflect.Ptr, so it fell through to the default branch and was
copied verbatim, leaving raw *T pointers in the output map while the equivalent
[]T field was correctly turned into []map[string]any.

Treat []*T (pointer-to-struct element) the same as []T. Extends the existing
TestDecode_structArrayDeepMap with a []*SourceChild field that fails before this
change and passes after. Fixes go-viper#196.
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

Successfully merging this pull request may close these issues.

Struct-to-Map conversion with Deep: true fails to recursively decode slices of pointers to structs

1 participant