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  }