@@ -10,10 +10,11 @@ import (
1010)
1111
1212type structCache struct {
13- indexes [][]int // field path (support for embedded structs)
14- names []string
15- omits []bool
16- noOmit bool
13+ indexes [][]int // field path (support for embedded structs)
14+ names []string
15+ omits []bool
16+ omitPaths [][][]int // embedded omitempty parent paths
17+ noOmit bool
1718 common.Common
1819}
1920
@@ -22,20 +23,31 @@ var cachemap = sync.Map{}
2223type structCalcFunc func (rv reflect.Value ) (int , error )
2324type structWriteFunc func (rv reflect.Value , offset int ) int
2425
25- // getFieldByPath returns the field value by following the path of indices
26- func getFieldByPath (rv reflect.Value , path []int ) reflect.Value {
26+ // getFieldByPath returns the field value by following the path of indices.
27+ // The bool indicates whether the path was reachable (no nil pointer in the path).
28+ func getFieldByPath (rv reflect.Value , path []int ) (reflect.Value , bool ) {
2729 for _ , idx := range path {
2830 // Handle pointer indirection if needed
2931 if rv .Kind () == reflect .Ptr {
3032 if rv .IsNil () {
31- // Return zero value if pointer is nil
32- return reflect.Value {}
33+ // Return invalid value if pointer is nil
34+ return reflect.Value {}, false
3335 }
3436 rv = rv .Elem ()
3537 }
3638 rv = rv .Field (idx )
3739 }
38- return rv
40+ return rv , true
41+ }
42+
43+ func shouldOmitByParent (rv reflect.Value , omitPaths [][]int ) bool {
44+ for _ , path := range omitPaths {
45+ parentValue , ok := getFieldByPath (rv , path )
46+ if ! ok || parentValue .IsZero () {
47+ return true
48+ }
49+ }
50+ return false
3951}
4052
4153func (e * encoder ) getStructCalc (typ reflect.Type ) structCalcFunc {
@@ -81,18 +93,10 @@ func (e *encoder) calcStructArray(rv reflect.Value) (int, error) {
8193 fields := e .CollectFields (t , nil )
8294 omitCount := 0
8395 for _ , field := range fields {
84- fieldValue := getFieldByPath (rv , field .Path )
85- if ! fieldValue .IsValid () {
86- continue
87- }
88- size , err := e .calcSize (fieldValue )
89- if err != nil {
90- return 0 , err
91- }
92- ret += size
9396 c .indexes = append (c .indexes , field .Path )
9497 c .names = append (c .names , field .Name )
9598 c .omits = append (c .omits , field .Omit )
99+ c .omitPaths = append (c .omitPaths , field .OmitPaths )
96100 if field .Omit {
97101 omitCount ++
98102 }
@@ -101,17 +105,17 @@ func (e *encoder) calcStructArray(rv reflect.Value) (int, error) {
101105 cachemap .Store (t , c )
102106 } else {
103107 c = cache .(* structCache )
104- for i := 0 ; i < len (c .indexes ); i ++ {
105- fieldValue := getFieldByPath (rv , c .indexes [i ])
106- if ! fieldValue .IsValid () {
107- continue
108- }
109- size , err := e .calcSize (fieldValue )
110- if err != nil {
111- return 0 , err
112- }
113- ret += size
108+ }
109+ for i := 0 ; i < len (c .indexes ); i ++ {
110+ fieldValue , ok := getFieldByPath (rv , c .indexes [i ])
111+ if shouldOmitByParent (rv , c .omitPaths [i ]) || ! ok {
112+ fieldValue = reflect.Value {}
113+ }
114+ size , err := e .calcSize (fieldValue )
115+ if err != nil {
116+ return 0 , err
114117 }
118+ ret += size
115119 }
116120
117121 // format size
@@ -128,53 +132,42 @@ func (e *encoder) calcStructMap(rv reflect.Value) (int, error) {
128132 t := rv .Type ()
129133 cache , find := cachemap .Load (t )
130134 var c * structCache
131- var l int
132135 if ! find {
133136 c = & structCache {}
134137 fields := e .CollectFields (t , nil )
135138 omitCount := 0
136139 for _ , field := range fields {
137- fieldValue := getFieldByPath (rv , field .Path )
138- if ! fieldValue .IsValid () {
139- continue
140- }
141- size , err := e .calcSizeWithOmitEmpty (fieldValue , field .Name , field .Omit )
142- if err != nil {
143- return 0 , err
144- }
145- ret += size
146140 c .indexes = append (c .indexes , field .Path )
147141 c .names = append (c .names , field .Name )
148142 c .omits = append (c .omits , field .Omit )
143+ c .omitPaths = append (c .omitPaths , field .OmitPaths )
149144 if field .Omit {
150145 omitCount ++
151146 }
152- if size > 0 {
153- l ++
154- }
155147 }
156148 c .noOmit = omitCount == 0
157149 cachemap .Store (t , c )
158150 } else {
159151 c = cache .(* structCache )
160- for i := 0 ; i < len (c .indexes ); i ++ {
161- fieldValue := getFieldByPath (rv , c .indexes [i ])
162- if ! fieldValue .IsValid () {
163- continue
164- }
165- size , err := e .calcSizeWithOmitEmpty (fieldValue , c .names [i ], c .omits [i ])
166- if err != nil {
167- return 0 , err
168- }
169- ret += size
170- if size > 0 {
171- l ++
172- }
152+ }
153+ l := 0
154+ for i := 0 ; i < len (c .indexes ); i ++ {
155+ fieldValue , ok := getFieldByPath (rv , c .indexes [i ])
156+ if shouldOmitByParent (rv , c .omitPaths [i ]) || ! ok {
157+ continue
158+ }
159+ size , err := e .calcSizeWithOmitEmpty (fieldValue , c .names [i ], c .omits [i ])
160+ if err != nil {
161+ return 0 , err
162+ }
163+ ret += size
164+ if size > 0 {
165+ l ++
173166 }
174167 }
175168
176169 // format size
177- size , err := e .calcLength (len ( c . indexes ) )
170+ size , err := e .calcLength (l )
178171 if err != nil {
179172 return 0 , err
180173 }
@@ -249,7 +242,10 @@ func (e *encoder) writeStructArray(rv reflect.Value, offset int) int {
249242 }
250243
251244 for i := 0 ; i < num ; i ++ {
252- fieldValue := getFieldByPath (rv , c .indexes [i ])
245+ fieldValue , ok := getFieldByPath (rv , c .indexes [i ])
246+ if shouldOmitByParent (rv , c .omitPaths [i ]) || ! ok {
247+ fieldValue = reflect.Value {}
248+ }
253249 offset = e .create (fieldValue , offset )
254250 }
255251 return offset
@@ -263,14 +259,13 @@ func (e *encoder) writeStructMap(rv reflect.Value, offset int) int {
263259 // format size
264260 num := len (c .indexes )
265261 l := 0
266- if c .noOmit {
267- l = num
268- } else {
269- for i := 0 ; i < num ; i ++ {
270- fieldValue := getFieldByPath (rv , c .indexes [i ])
271- if ! c .omits [i ] || ! fieldValue .IsZero () {
272- l ++
273- }
262+ for i := 0 ; i < num ; i ++ {
263+ fieldValue , ok := getFieldByPath (rv , c .indexes [i ])
264+ if shouldOmitByParent (rv , c .omitPaths [i ]) || ! ok {
265+ continue
266+ }
267+ if c .noOmit || ! c .omits [i ] || ! fieldValue .IsZero () {
268+ l ++
274269 }
275270 }
276271
@@ -285,8 +280,11 @@ func (e *encoder) writeStructMap(rv reflect.Value, offset int) int {
285280 }
286281
287282 for i := 0 ; i < num ; i ++ {
288- fieldValue := getFieldByPath (rv , c .indexes [i ])
289- if ! c .omits [i ] || ! fieldValue .IsZero () {
283+ fieldValue , ok := getFieldByPath (rv , c .indexes [i ])
284+ if shouldOmitByParent (rv , c .omitPaths [i ]) || ! ok {
285+ continue
286+ }
287+ if c .noOmit || ! c .omits [i ] || ! fieldValue .IsZero () {
290288 offset = e .writeString (c .names [i ], offset )
291289 offset = e .create (fieldValue , offset )
292290 }
0 commit comments