github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/jsoni/reflect_struct_encoder.go (about) 1 package jsoni 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "fmt" 8 "io" 9 "reflect" 10 "unsafe" 11 12 "github.com/modern-go/reflect2" 13 ) 14 15 func encoderOfStruct(ctx *ctx, typ reflect2.Type) ValEncoder { 16 type bindingTo struct { 17 binding *Binding 18 toName string 19 ignored bool 20 } 21 var orderedBindings []*bindingTo 22 structDescriptor := describeStruct(ctx, typ) 23 for _, binding := range structDescriptor.Fields { 24 for _, toName := range binding.ToNames { 25 b := &bindingTo{binding: binding, toName: toName} 26 for _, old := range orderedBindings { 27 if old.toName != toName { 28 continue 29 } 30 old.ignored, b.ignored = resolveConflictBinding(ctx.frozenConfig, old.binding, b.binding) 31 } 32 orderedBindings = append(orderedBindings, b) 33 } 34 } 35 if len(orderedBindings) == 0 { 36 return &emptyStructEncoder{} 37 } 38 var finalOrderedFields []structFieldTo 39 for _, b := range orderedBindings { 40 if !b.ignored { 41 finalOrderedFields = append(finalOrderedFields, structFieldTo{ 42 encoder: b.binding.Encoder.(*structFieldEncoder), 43 toName: b.toName, 44 }) 45 } 46 } 47 return &structEncoder{typ: typ, fields: finalOrderedFields} 48 } 49 50 func createCheckIsEmpty(ctx *ctx, typ reflect2.Type) checkIsEmpty { 51 if e := createEncoderOfNative(ctx, typ); e != nil { 52 return e 53 } 54 55 switch kind := typ.Kind(); kind { 56 case reflect.Interface: 57 return &dynamicEncoder{valType: typ} 58 case reflect.Struct: 59 return &structEncoder{typ: typ} 60 case reflect.Array: 61 return &arrayEncoder{} 62 case reflect.Slice: 63 return &sliceEncoder{ctx: ctx} 64 case reflect.Map: 65 return encoderOfMap(ctx, typ) 66 case reflect.Ptr: 67 return &OptionalEncoder{} 68 default: 69 return &lazyErrorEncoder{err: fmt.Errorf("unsupported type: %v", typ)} 70 } 71 } 72 73 func resolveConflictBinding(cfg *frozenConfig, old, new *Binding) (ignoreOld, ignoreNew bool) { 74 newTagged := new.Field.Tag().Get(cfg.getTagKey()) != "" 75 oldTagged := old.Field.Tag().Get(cfg.getTagKey()) != "" 76 if newTagged { 77 if oldTagged { 78 if len(old.levels) > len(new.levels) { 79 return true, false 80 } else if len(new.levels) > len(old.levels) { 81 return false, true 82 } else { 83 return true, true 84 } 85 } else { 86 return true, false 87 } 88 } else { 89 if oldTagged { 90 return true, false 91 } 92 if len(old.levels) > len(new.levels) { 93 return true, false 94 } else if len(new.levels) > len(old.levels) { 95 return false, true 96 } else { 97 return true, true 98 } 99 } 100 } 101 102 type structFieldEncoder struct { 103 field reflect2.StructField 104 fieldEncoder ValEncoder 105 omitempty bool 106 nilAsEmpty bool 107 clearQuotes bool 108 } 109 110 type key int 111 112 const ( 113 _ key = iota 114 keyStructField 115 ) 116 117 func getContextFrozenConfig(ctx context.Context) *frozenConfig { 118 cv := ctx.Value(ContextCfg) 119 if cv != nil { 120 return cv.(*frozenConfig) 121 } 122 123 return &frozenConfig{} 124 } 125 126 func getContextStructFieldEncoder(ctx context.Context) *structFieldEncoder { 127 if v := ctx.Value(keyStructField); v != nil { 128 return v.(*structFieldEncoder) 129 } 130 return &structFieldEncoder{} 131 } 132 133 func getContextClearQuotes(ctx context.Context) bool { 134 return getContextStructFieldEncoder(ctx).clearQuotes || getContextFrozenConfig(ctx).clearQuotes 135 } 136 137 func getContextNilEmpty(ctx context.Context) bool { 138 return getContextStructFieldEncoder(ctx).nilAsEmpty || getContextFrozenConfig(ctx).nilAsEmpty 139 } 140 141 func writeRawBytesIfClearQuotes(ctx context.Context, s string, stream *Stream) bool { 142 if s != "" && getContextClearQuotes(ctx) { 143 if b := []byte(s); ValidJSONContext(ctx, b) { 144 buf := &bytes.Buffer{} 145 _ = json.Compact(buf, b) 146 stream.WriteRawBytes(buf.Bytes()) 147 return true 148 } 149 } 150 151 return false 152 } 153 154 func (e *structFieldEncoder) Encode(ctx context.Context, ptr unsafe.Pointer, stream *Stream) { 155 ctx = context.WithValue(ctx, keyStructField, e) 156 157 fieldPtr := e.field.UnsafeGet(ptr) 158 e.fieldEncoder.Encode(ctx, fieldPtr, stream) 159 160 if stream.Error != nil && stream.Error != io.EOF { 161 stream.Error = fmt.Errorf("%s: %s", e.field.Name(), stream.Error.Error()) 162 } 163 } 164 165 func (e *structFieldEncoder) IsEmpty(ctx context.Context, ptr unsafe.Pointer, checkZero bool) bool { 166 fieldPtr := e.field.UnsafeGet(ptr) 167 return e.fieldEncoder.IsEmpty(ctx, fieldPtr, checkZero) 168 } 169 170 func (e *structFieldEncoder) IsEmbeddedPtrNil(ptr unsafe.Pointer) bool { 171 isEmbeddedPtrNil, converted := e.fieldEncoder.(IsEmbeddedPtrNil) 172 if !converted { 173 return false 174 } 175 fieldPtr := e.field.UnsafeGet(ptr) 176 return isEmbeddedPtrNil.IsEmbeddedPtrNil(fieldPtr) 177 } 178 179 type IsEmbeddedPtrNil interface { 180 IsEmbeddedPtrNil(ptr unsafe.Pointer) bool 181 } 182 183 type structEncoder struct { 184 typ reflect2.Type 185 fields []structFieldTo 186 } 187 188 type structFieldTo struct { 189 encoder *structFieldEncoder 190 toName string 191 } 192 193 func (e *structEncoder) Encode(ctx context.Context, ptr unsafe.Pointer, stream *Stream) { 194 stream.WriteObjectStart() 195 isNotFirst := false 196 for _, field := range e.fields { 197 fe := field.encoder 198 if fe.omitempty && fe.IsEmpty(ctx, ptr, true) { 199 continue 200 } 201 if fe.IsEmbeddedPtrNil(ptr) { 202 continue 203 } 204 if isNotFirst { 205 stream.WriteMore() 206 } 207 stream.WriteObjectField(field.toName) 208 209 fe.Encode(ctx, ptr, stream) 210 isNotFirst = true 211 } 212 stream.WriteObjectEnd() 213 if stream.Error != nil && stream.Error != io.EOF { 214 stream.Error = fmt.Errorf("%v.%s", e.typ, stream.Error.Error()) 215 } 216 } 217 218 func (e *structEncoder) IsEmpty(context.Context, unsafe.Pointer, bool) bool { return false } 219 220 type emptyStructEncoder struct{} 221 222 func (e *emptyStructEncoder) Encode(_ context.Context, _ unsafe.Pointer, stream *Stream) { 223 stream.WriteEmptyObject() 224 } 225 func (e *emptyStructEncoder) IsEmpty(context.Context, unsafe.Pointer, bool) bool { return false } 226 227 type stringModeNumberEncoder struct { 228 elemEncoder ValEncoder 229 } 230 231 func (e *stringModeNumberEncoder) Encode(ctx context.Context, ptr unsafe.Pointer, stream *Stream) { 232 stream.writeByte('"') 233 e.elemEncoder.Encode(ctx, ptr, stream) 234 stream.writeByte('"') 235 } 236 237 func (e *stringModeNumberEncoder) IsEmpty(ctx context.Context, p unsafe.Pointer, checkZero bool) bool { 238 return e.elemEncoder.IsEmpty(ctx, p, checkZero) 239 } 240 241 type stringModeStringEncoder struct { 242 encoder ValEncoder 243 cfg *frozenConfig 244 } 245 246 func (e *stringModeStringEncoder) Encode(ctx context.Context, ptr unsafe.Pointer, stream *Stream) { 247 temp := e.cfg.BorrowStream(nil) 248 temp.Attachment = stream.Attachment 249 defer e.cfg.ReturnStream(temp) 250 e.encoder.Encode(ctx, ptr, temp) 251 stream.WriteString(string(temp.Buffer())) 252 } 253 254 func (e *stringModeStringEncoder) IsEmpty(ctx context.Context, ptr unsafe.Pointer, checkZero bool) bool { 255 return e.encoder.IsEmpty(ctx, ptr, checkZero) 256 }