mirror of
https://github.com/yusing/godoxy.git
synced 2025-05-20 04:42:33 +02:00
148 lines
3.2 KiB
Go
148 lines
3.2 KiB
Go
package json
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
|
|
"github.com/yusing/go-proxy/internal/utils/strutils"
|
|
)
|
|
|
|
type field struct {
|
|
quotedNameWithCol string
|
|
|
|
index int
|
|
inner []*field
|
|
hasInner bool
|
|
isPtr bool
|
|
omitEmpty bool // true when json tag is "omitempty" or field is pointer to anonymous struct
|
|
checkEmpty checkEmptyFunc
|
|
marshal marshalFunc
|
|
}
|
|
|
|
func (f *field) appendKV(v reflect.Value, buf []byte) []byte {
|
|
return f.marshal(v, append(buf, f.quotedNameWithCol...))
|
|
}
|
|
|
|
const (
|
|
tagOmitEmpty = "omitempty"
|
|
tagString = "string" // https://pkg.go.dev/github.com/yusing/go-proxy/pkg/json#Marshal
|
|
tagByteSize = "byte_size"
|
|
tagUnixTime = "unix_time"
|
|
tagUseMarshaler = "use_marshaler"
|
|
)
|
|
|
|
func appendStruct(v reflect.Value, buf []byte) []byte {
|
|
if res, ok := appendWithCachedFunc(v, buf); ok {
|
|
return res
|
|
}
|
|
|
|
if res, ok := appendWithCustomMarshaler(v, buf); ok {
|
|
return res
|
|
}
|
|
|
|
t := v.Type()
|
|
fields := flattenFields(t)
|
|
marshalFn := func(v reflect.Value, buf []byte) []byte {
|
|
return appendFields(v, fields, buf)
|
|
}
|
|
cacheMarshalFunc(t, marshalFn)
|
|
return marshalFn(v, buf)
|
|
}
|
|
|
|
func appendFields(v reflect.Value, fields []*field, buf []byte) []byte {
|
|
buf = append(buf, '{')
|
|
oldN := len(buf)
|
|
|
|
for _, f := range fields {
|
|
cur := v.Field(f.index)
|
|
if f.omitEmpty && f.checkEmpty(cur) {
|
|
continue
|
|
}
|
|
if !f.hasInner {
|
|
buf = f.appendKV(cur, buf)
|
|
buf = append(buf, ',')
|
|
continue
|
|
}
|
|
if f.isPtr {
|
|
cur = cur.Elem()
|
|
}
|
|
for _, inner := range f.inner {
|
|
buf = inner.appendKV(cur.Field(inner.index), buf)
|
|
buf = append(buf, ',')
|
|
}
|
|
}
|
|
|
|
n := len(buf)
|
|
if oldN != n {
|
|
buf = buf[:n-1]
|
|
}
|
|
return append(buf, '}')
|
|
}
|
|
|
|
func flattenFields(t reflect.Type) []*field {
|
|
n := t.NumField()
|
|
fields := make([]*field, 0, n)
|
|
for i := range n {
|
|
structField := t.Field(i)
|
|
if !structField.IsExported() {
|
|
continue
|
|
}
|
|
t := structField.Type
|
|
kind := t.Kind()
|
|
f := &field{
|
|
index: i,
|
|
isPtr: kind == reflect.Pointer,
|
|
}
|
|
jsonTag, ok := structField.Tag.Lookup("json")
|
|
if ok {
|
|
if jsonTag == "-" {
|
|
continue
|
|
}
|
|
parts := strutils.SplitComma(jsonTag)
|
|
if len(parts) > 1 {
|
|
switch parts[1] {
|
|
case tagOmitEmpty:
|
|
f.omitEmpty = true
|
|
f.checkEmpty = checkEmptyFuncs[kind]
|
|
case tagString:
|
|
f.marshal = appendStringRepr
|
|
case tagByteSize:
|
|
f.marshal = appendByteSize
|
|
case tagUnixTime:
|
|
f.marshal = appendUnixTime
|
|
case tagUseMarshaler:
|
|
f.marshal = mustAppendWithCustomMarshaler
|
|
default:
|
|
panic(fmt.Errorf("unknown json tag: %s", parts[1]))
|
|
}
|
|
f.quotedNameWithCol = parts[0]
|
|
} else {
|
|
f.quotedNameWithCol = jsonTag
|
|
}
|
|
}
|
|
|
|
if f.quotedNameWithCol == "" { // e.g. json:",omitempty"
|
|
f.quotedNameWithCol = structField.Name
|
|
}
|
|
if f.marshal == nil {
|
|
f.marshal = appendMarshal
|
|
}
|
|
if structField.Anonymous {
|
|
if kind == reflect.Pointer {
|
|
t = t.Elem()
|
|
kind = t.Kind()
|
|
f.isPtr = true
|
|
f.omitEmpty = true
|
|
f.checkEmpty = checkEmptyFuncs[kind]
|
|
}
|
|
if kind == reflect.Struct {
|
|
f.inner = flattenFields(t)
|
|
f.hasInner = len(f.inner) > 0
|
|
}
|
|
}
|
|
fields = append(fields, f)
|
|
quotedNameWithCol := AppendString(nil, f.quotedNameWithCol)
|
|
f.quotedNameWithCol = string(quotedNameWithCol) + ":"
|
|
}
|
|
return fields
|
|
}
|