From ffb9dd3a16f8ac3b49f61f3ab99dde6b8286b989 Mon Sep 17 00:00:00 2001 From: Fulenn <110468349+Fulenn@users.noreply.github.com> Date: Thu, 17 Sep 2026 11:20:08 +0200 Subject: [PATCH] fix: handle nil values in recursive struct decode hooks --- decode_hooks.go | 2 +- decode_hooks_test.go | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/decode_hooks.go b/decode_hooks.go index a3dcc133..c7915107 100644 --- a/decode_hooks.go +++ b/decode_hooks.go @@ -380,7 +380,7 @@ func WeaklyTypedHook( func RecursiveStructToMapHookFunc() DecodeHookFunc { return func(f reflect.Value, t reflect.Value) (any, error) { if f.Kind() != reflect.Struct { - return f.Interface(), nil + return safeInterface(f), nil } var i any = struct{}{} diff --git a/decode_hooks_test.go b/decode_hooks_test.go index 02d5e662..6749b97b 100644 --- a/decode_hooks_test.go +++ b/decode_hooks_test.go @@ -889,6 +889,49 @@ func TestWeaklyTypedHook(t *testing.T) { } } +func TestRecursiveStructToMapHookFunc_nil(t *testing.T) { + cases := []struct { + name string + hook DecodeHookFunc + }{ + {"standalone", RecursiveStructToMapHookFunc()}, + {"composed", ComposeDecodeHookFunc( + StringToTimeDurationHookFunc(), + RecursiveStructToMapHookFunc(), + )}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + input := map[string]any{ + "nil": nil, + "nested": struct { + Value string + }{Value: "hello"}, + } + var result map[string]any + decoder, err := NewDecoder(&DecoderConfig{ + DecodeHook: tc.hook, + DecodeNil: true, + Result: &result, + }) + if err != nil { + t.Fatal(err) + } + if err := decoder.Decode(input); err != nil { + t.Fatal(err) + } + want := map[string]any{ + "nil": nil, + "nested": map[string]any{"Value": "hello"}, + } + if !reflect.DeepEqual(result, want) { + t.Fatalf("expected %#v, got %#v", want, result) + } + }) + } +} + func TestStructToMapHookFuncTabled(t *testing.T) { var f DecodeHookFunc = RecursiveStructToMapHookFunc()