Skip to content

Commit 17f44b6

Browse files
committed
修复对nil interface{}解析时panic的问题
1 parent d459a50 commit 17f44b6

File tree

11 files changed

+393
-78
lines changed

11 files changed

+393
-78
lines changed

filter/cache.go

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
package filter
2+
3+
import (
4+
"reflect"
5+
)
6+
7+
var fieldCacheVar fieldCache
8+
9+
func init() {
10+
fieldCacheVar.maps = make(map[string]*field)
11+
}
12+
13+
type fieldCache struct {
14+
maps map[string]*field
15+
}
16+
type field struct {
17+
index int //该字段所处结构体的索引位置
18+
tag tag
19+
selectFields []*field
20+
}
21+
22+
func newField(t tag, idx int) field {
23+
return field{
24+
tag: t,
25+
index: idx,
26+
}
27+
}
28+
29+
func getCacheKey(pkgInfo string, scene string, isSelect bool) string {
30+
mode := ".s"
31+
if !isSelect {
32+
mode = ".o"
33+
}
34+
return pkgInfo + "." + scene + mode
35+
}
36+
func (t *fieldNodeTree) parseAny2(key, scene string, valueOf reflect.Value, isSelect bool) {
37+
typeOf := valueOf.Type()
38+
TakePointerValue: //取指针的值
39+
switch typeOf.Kind() {
40+
case reflect.Ptr: //如果是指针类型则取值重新判断类型
41+
valueOf = valueOf.Elem()
42+
typeOf = typeOf.Elem()
43+
goto TakePointerValue
44+
case reflect.Interface:
45+
if !valueOf.IsNil() {
46+
valueOf = reflect.ValueOf(valueOf.Interface())
47+
typeOf = valueOf.Type()
48+
goto TakePointerValue
49+
} else {
50+
parserNilInterface(t, key)
51+
}
52+
case reflect.Struct:
53+
parserStructCache(typeOf, valueOf, t, scene, key, isSelect)
54+
case reflect.Bool,
55+
reflect.String,
56+
reflect.Float64, reflect.Float32,
57+
reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int,
58+
reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
59+
parserBaseType(valueOf, t, key)
60+
case reflect.Map:
61+
parserMap(valueOf, t, scene, isSelect)
62+
case reflect.Slice, reflect.Array:
63+
parserSliceOrArray(typeOf, valueOf, t, scene, key, isSelect)
64+
}
65+
66+
}
67+
68+
func parserStructCache(typeOf reflect.Type, valueOf reflect.Value, t *fieldNodeTree, scene string, key string, isSelect bool) {
69+
if valueOf.CanConvert(timeTypes) { //是time.Time类型或者底层是time.Time类型
70+
t.Key = key
71+
t.Val = valueOf.Interface()
72+
return
73+
}
74+
if typeOf.NumField() == 0 { //如果是一个struct{}{}类型的字段或者是一个空的自定义结构体编码为{}
75+
t.Key = key
76+
t.Val = struct{}{}
77+
return
78+
}
79+
pkgInfo := typeOf.PkgPath() + "." + typeOf.Name()
80+
81+
cacheKey := getCacheKey(pkgInfo, scene, isSelect)
82+
cacheField, ok := fieldCacheVar.maps[cacheKey]
83+
if !ok {
84+
85+
var parentField *field
86+
if t.isRoot {
87+
//fieldCacheVar.maps[cacheKey] = &field{}
88+
parentField = new(field)
89+
fieldCacheVar.maps[cacheKey] = parentField
90+
} else {
91+
parentField = t.fieldCache
92+
}
93+
94+
for i := 0; i < typeOf.NumField(); i++ {
95+
var tagInfo tagInfo
96+
tagInfo = getSelectTag(scene, pkgInfo, i, typeOf)
97+
if !isSelect {
98+
tagInfo = getOmitTag(scene, pkgInfo, i, typeOf)
99+
}
100+
101+
if tagInfo.omit {
102+
continue
103+
}
104+
tag := tagInfo.tag
105+
if tag.IsOmitField || !tag.IsSelect {
106+
continue
107+
}
108+
isAnonymous := typeOf.Field(i).Anonymous && tag.IsAnonymous ////什么时候才算真正的匿名字段? Book中Article才算匿名结构体
109+
tag.IsAnonymous = isAnonymous
110+
fieldCache := newField(tag, i)
111+
fieldEl, ok1 := fieldCacheVar.maps[cacheKey]
112+
if ok1 {
113+
fieldEl.selectFields = append(fieldEl.selectFields, &fieldCache)
114+
//fieldCacheVar.maps[cacheKey].selectFields = append(fieldCacheVar.maps[cacheKey].selectFields, &fieldCache)
115+
}
116+
117+
tree := &fieldNodeTree{
118+
Key: tag.UseFieldName,
119+
ParentNode: t,
120+
IsAnonymous: isAnonymous,
121+
Tag: tag,
122+
fieldCache: &fieldCache,
123+
}
124+
value := valueOf.Field(i)
125+
if tag.Function != "" {
126+
function := valueOf.MethodByName(tag.Function)
127+
if !function.IsValid() {
128+
if valueOf.CanAddr() {
129+
function = valueOf.Addr().MethodByName(tag.Function)
130+
}
131+
}
132+
if function.IsValid() {
133+
values := function.Call([]reflect.Value{})
134+
if len(values) != 0 {
135+
value = values[0]
136+
}
137+
}
138+
}
139+
if value.Kind() == reflect.Ptr {
140+
TakeFieldValue:
141+
if value.Kind() == reflect.Ptr {
142+
if value.IsNil() {
143+
if tag.Omitempty {
144+
continue
145+
}
146+
tree.IsNil = true
147+
t.AddChild(tree)
148+
continue
149+
} else {
150+
value = value.Elem()
151+
goto TakeFieldValue
152+
}
153+
}
154+
} else {
155+
if tag.Omitempty {
156+
if value.IsZero() { //为零值忽略
157+
continue
158+
}
159+
}
160+
}
161+
162+
tree.parseAny2(tag.UseFieldName, scene, value, isSelect)
163+
164+
if t.IsAnonymous {
165+
t.AnonymousAddChild(tree)
166+
} else {
167+
t.AddChild(tree)
168+
}
169+
}
170+
} else { //说明缓存取到
171+
for i := 0; i < len(cacheField.selectFields); i++ {
172+
fieldInfo := cacheField.selectFields[i]
173+
tag := fieldInfo.tag
174+
tree := &fieldNodeTree{
175+
Key: tag.UseFieldName,
176+
ParentNode: t,
177+
IsAnonymous: tag.IsAnonymous,
178+
Tag: tag,
179+
fieldCache: fieldInfo,
180+
}
181+
value := valueOf.Field(fieldInfo.index)
182+
if tag.Function != "" {
183+
function := valueOf.MethodByName(tag.Function)
184+
if !function.IsValid() {
185+
if valueOf.CanAddr() {
186+
function = valueOf.Addr().MethodByName(tag.Function)
187+
}
188+
}
189+
if function.IsValid() {
190+
values := function.Call([]reflect.Value{})
191+
if len(values) != 0 {
192+
value = values[0]
193+
}
194+
}
195+
}
196+
if value.Kind() == reflect.Ptr {
197+
TakeFieldValue1:
198+
if value.Kind() == reflect.Ptr {
199+
if value.IsNil() {
200+
if tag.Omitempty {
201+
continue
202+
}
203+
tree.IsNil = true
204+
t.AddChild(tree)
205+
continue
206+
} else {
207+
value = value.Elem()
208+
goto TakeFieldValue1
209+
}
210+
}
211+
} else {
212+
if tag.Omitempty {
213+
if value.IsZero() { //为零值忽略
214+
continue
215+
}
216+
}
217+
}
218+
219+
tree.parseAny2(tag.UseFieldName, scene, value, isSelect)
220+
221+
if t.IsAnonymous {
222+
t.AnonymousAddChild(tree)
223+
} else {
224+
t.AddChild(tree)
225+
}
226+
}
227+
}
228+
229+
if t.Children == nil && !t.IsAnonymous {
230+
t.Val = struct{}{} //这样表示返回{}
231+
}
232+
233+
}

filter/filter.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ func Select(selectScene string, el interface{}) interface{} {
1717
func jsonFilter(selectScene string, el interface{}, isSelect bool) Filter {
1818
tree := &fieldNodeTree{
1919
Key: "",
20+
isRoot: true,
2021
ParentNode: nil,
2122
}
2223
tree.parseAny("", selectScene, reflect.ValueOf(el), isSelect)
@@ -81,3 +82,25 @@ func (f Filter) String() string {
8182
}
8283
return json
8384
}
85+
86+
//// SelectCache 直接返回过滤后的数据结构,它可以被json.Marshal解析,直接打印会以过滤后的json字符串展示
87+
//func SelectCache(selectScene string, el interface{}) interface{} {
88+
// return jsonFilterCache(selectScene, el, true)
89+
//}
90+
//
91+
//func jsonFilterCache(selectScene string, el interface{}, isSelect bool) Filter {
92+
// tree := &fieldNodeTree{
93+
// Key: "",
94+
// isRoot: true,
95+
// ParentNode: nil,
96+
// }
97+
// tree.parseAny2("", selectScene, reflect.ValueOf(el), isSelect)
98+
// return Filter{
99+
// node: tree,
100+
// }
101+
//}
102+
//
103+
//// OmitCache 直接返回过滤后的数据结构,它可以被json.Marshal解析,直接打印会以过滤后的json字符串展示
104+
//func OmitCache(selectScene string, el interface{}) interface{} {
105+
// return jsonFilterCache(selectScene, el, false)
106+
//}

filter/json_marshal_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func TestJsonMarshal(t *testing.T) {
2020
Lang: "Go",
2121
}
2222

23-
f := SelectMarshal("lang", user)
23+
f := Select("lang", user)
2424
marshal, err := json.Marshal(f)
2525
if err != nil {
2626
panic(err)

filter/node_decode.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,16 @@ import (
66

77
type fieldNodeTree struct {
88
isSelect bool //是否是select 方法
9+
isRoot bool //是过滤的入口结构体
910
Key string //字段名
1011
Val interface{} //字段值,基础数据类型,int string,bool 等类型直接存在这里面,如果是struct,切片数组map 类型则字段所有k v会存在ChildNodes里
1112
IsSlice bool //是否是切片,或者数组,
1213
IsAnonymous bool //是否是匿名结构体,内嵌结构体,需要把所有字段展开
1314
IsNil bool //该字段值是否为nil
1415
ParentNode *fieldNodeTree //父节点指针,根节点为nil,
1516
Children []*fieldNodeTree //如果是struct则保存所有字段名和值的指针,如果是切片就保存切片的所有值
17+
Tag tag //标签
18+
fieldCache *field //缓存
1619
}
1720

1821
func (t *fieldNodeTree) GetValue() (val interface{}, ok bool) {

filter/parser.go

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,14 @@ TakePointerValue: //取指针的值
1919
typeOf = typeOf.Elem()
2020
goto TakePointerValue
2121
case reflect.Interface:
22-
valueOf = reflect.ValueOf(valueOf.Interface())
23-
typeOf = valueOf.Type()
24-
goto TakePointerValue
22+
if !valueOf.IsNil() {
23+
valueOf = reflect.ValueOf(valueOf.Interface())
24+
typeOf = valueOf.Type()
25+
goto TakePointerValue
26+
} else {
27+
parserNilInterface(t, key)
28+
}
29+
2530
case reflect.Struct:
2631
parserStruct(typeOf, valueOf, t, scene, key, isSelect)
2732
case reflect.Bool,
@@ -38,6 +43,22 @@ TakePointerValue: //取指针的值
3843

3944
}
4045

46+
func parserNilInterface(t *fieldNodeTree, key string) {
47+
if t.IsAnonymous {
48+
tree := &fieldNodeTree{
49+
Key: t.Key,
50+
ParentNode: t,
51+
Val: t.Val,
52+
IsNil: true,
53+
}
54+
t.AnonymousAddChild(tree)
55+
} else {
56+
t.Val = nil
57+
t.Key = key
58+
t.IsNil = true
59+
}
60+
}
61+
4162
func getFieldOmitTag(field reflect.StructField, scene string) tagInfo {
4263
tagInfoEl := tagInfo{}
4364
//没开缓存就获取tag

filter/tag.go

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,20 @@ const (
1010
)
1111

1212
type tag struct {
13-
//执行的场景
14-
SelectScene string
15-
//该字段是否需要被忽略?
16-
IsOmitField bool
17-
//是选中的情况,标识该字段是否需要被解析
18-
IsSelect bool
19-
//字段名称
20-
UseFieldName string
21-
//IsAnonymous 标识该字段是否是匿名字段
22-
IsAnonymous bool
23-
////为空忽略
24-
Omitempty bool
25-
//自定义处理函数
26-
Function string
13+
SelectScene string //执行的场景
14+
IsOmitField bool //该字段是否需要被忽略?
15+
IsSelect bool //是选中的情况,标识该字段是否需要被解析
16+
FieldName string //字段名
17+
UseFieldName string //最终使用的字段名,没标签就用结构体字段名,tag里有标签就用标签名字
18+
IsAnonymous bool //IsAnonymous 标识该字段是否是匿名字段
19+
Omitempty bool //为空忽略
20+
Function string //自定义处理函数
2721
}
2822

2923
func newSelectTag(tagStr, selectScene, fieldName string) tag {
3024

3125
tagEl := tag{
26+
FieldName: fieldName,
3227
SelectScene: selectScene,
3328
IsOmitField: true,
3429
}
@@ -69,6 +64,7 @@ func newSelectTag(tagStr, selectScene, fieldName string) tag {
6964

7065
func newOmitTag(tagStr, omitScene, fieldName string) tag {
7166
tagEl := tag{
67+
FieldName: fieldName,
7268
SelectScene: omitScene,
7369
IsOmitField: false,
7470
IsSelect: true,
@@ -116,6 +112,7 @@ func newOmitTag(tagStr, omitScene, fieldName string) tag {
116112

117113
func newOmitNotTag(omitScene, fieldName string) tag {
118114
return tag{
115+
FieldName: fieldName,
119116
IsSelect: true,
120117
UseFieldName: fieldName,
121118
SelectScene: omitScene,

0 commit comments

Comments
 (0)