diff --git a/encoding/gjson/gjson_z_unit_test.go b/encoding/gjson/gjson_z_unit_test.go index 55faf5d0490..9bb6958e5b6 100644 --- a/encoding/gjson/gjson_z_unit_test.go +++ b/encoding/gjson/gjson_z_unit_test.go @@ -53,6 +53,21 @@ func Test_New(t *testing.T) { t.Assert(j.Get("k2"), "v2") t.Assert(j.Get("k3"), nil) }) + // https://github.com/gogf/gf/issues/3253 + gtest.C(t, func(t *gtest.T) { + type TestStruct struct { + Result []map[string]string `json:"result"` + } + ts := &TestStruct{ + Result: []map[string]string{ + { + "Name": "gf", + "Role": "", + }, + }, + } + gjson.New(ts) + }) } func Test_Valid(t *testing.T) { diff --git a/internal/utils/utils_reflect.go b/internal/utils/utils_reflect.go new file mode 100644 index 00000000000..c217e407b7b --- /dev/null +++ b/internal/utils/utils_reflect.go @@ -0,0 +1,26 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package utils + +import ( + "reflect" +) + +// CanCallIsNil Can reflect.Value call reflect.Value.IsNil. +// It can avoid reflect.Value.IsNil panics. +func CanCallIsNil(v interface{}) bool { + rv, ok := v.(reflect.Value) + if !ok { + return false + } + switch rv.Kind() { + case reflect.Interface, reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer: + return true + default: + return false + } +} diff --git a/internal/utils/utils_z_unit_test.go b/internal/utils/utils_z_unit_test.go index 95077e7abc0..259091d5fd0 100644 --- a/internal/utils/utils_z_unit_test.go +++ b/internal/utils/utils_z_unit_test.go @@ -8,7 +8,9 @@ package utils_test import ( "io" + "reflect" "testing" + "unsafe" "github.com/gogf/gf/v2/internal/utils" "github.com/gogf/gf/v2/test/gtest" @@ -71,3 +73,25 @@ func Test_RemoveSymbols(t *testing.T) { t.Assert(utils.RemoveSymbols(`-a-b我._a c1!@#$%^&*是()_+:帅";'.,哥'01`), `ab我ac1是帅哥01`) }) } + +func Test_CanCallIsNil(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var ( + iValue = "gf" + iChan = make(chan struct{}) + iFunc = func() {} + iMap = map[string]struct{}{} + iPtr = &iValue + iSlice = make([]struct{}, 0) + iUnsafePointer = unsafe.Pointer(&iValue) + ) + + t.Assert(utils.CanCallIsNil(reflect.ValueOf(iValue)), false) + t.Assert(utils.CanCallIsNil(reflect.ValueOf(iChan)), true) + t.Assert(utils.CanCallIsNil(reflect.ValueOf(iFunc)), true) + t.Assert(utils.CanCallIsNil(reflect.ValueOf(iMap)), true) + t.Assert(utils.CanCallIsNil(reflect.ValueOf(iPtr)), true) + t.Assert(utils.CanCallIsNil(reflect.ValueOf(iSlice)), true) + t.Assert(utils.CanCallIsNil(reflect.ValueOf(iUnsafePointer)), true) + }) +} diff --git a/util/gconv/gconv_map.go b/util/gconv/gconv_map.go index 7d52fcda3e2..33470ebd0c6 100644 --- a/util/gconv/gconv_map.go +++ b/util/gconv/gconv_map.go @@ -303,7 +303,7 @@ func doMapConvertForMapOrStructValue(in doMapConvertForMapOrStructValueInput) in ) switch { case mapKeyValue.IsZero(): - if mapKeyValue.IsNil() { + if utils.CanCallIsNil(mapKeyValue) && mapKeyValue.IsNil() { // quick check for nil value. mapValue = nil } else {