mirror of
https://github.com/yusing/godoxy.git
synced 2026-04-24 01:38:50 +02:00
refactor(serialization): optimize deserialization
This commit is contained in:
@@ -449,51 +449,58 @@ func Convert(src reflect.Value, dst reflect.Value, checkValidateTag bool) gperr.
|
|||||||
}
|
}
|
||||||
return mapUnmarshalValidate(obj, dst.Addr(), checkValidateTag)
|
return mapUnmarshalValidate(obj, dst.Addr(), checkValidateTag)
|
||||||
case srcKind == reflect.Slice: // slice to slice
|
case srcKind == reflect.Slice: // slice to slice
|
||||||
srcLen := src.Len()
|
return ConvertSlice(src, dst, checkValidateTag)
|
||||||
if srcLen == 0 {
|
|
||||||
dst.SetZero()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if dstT.Kind() != reflect.Slice {
|
|
||||||
return ErrUnsupportedConversion.Subject(dstT.String() + " to " + srcT.String())
|
|
||||||
}
|
|
||||||
var sliceErrs gperr.Builder
|
|
||||||
i := 0
|
|
||||||
gi.ReflectInitSlice(dst, srcLen, srcLen)
|
|
||||||
for j, v := range src.Seq2() {
|
|
||||||
err := Convert(v, dst.Index(i), checkValidateTag)
|
|
||||||
if err != nil {
|
|
||||||
sliceErrs.Add(err.Subjectf("[%d]", j))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
if err := sliceErrs.Error(); err != nil {
|
|
||||||
dst.SetLen(i) // shrink to number of elements that were successfully converted
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ErrUnsupportedConversion.Subjectf("%s to %s", srcT.String(), dstT.String())
|
return ErrUnsupportedConversion.Subjectf("%s to %s", srcT, dstT)
|
||||||
}
|
}
|
||||||
|
|
||||||
var parserType = reflect.TypeFor[strutils.Parser]()
|
func ConvertSlice(src reflect.Value, dst reflect.Value, checkValidateTag bool) gperr.Error {
|
||||||
|
if src.Kind() != reflect.Slice {
|
||||||
|
return Convert(src, dst, checkValidateTag)
|
||||||
|
}
|
||||||
|
|
||||||
|
srcLen := src.Len()
|
||||||
|
if srcLen == 0 {
|
||||||
|
dst.SetZero()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if dst.Kind() != reflect.Slice {
|
||||||
|
return ErrUnsupportedConversion.Subjectf("%s to %s", dst.Type(), src.Type())
|
||||||
|
}
|
||||||
|
|
||||||
|
var sliceErrs gperr.Builder
|
||||||
|
numValid := 0
|
||||||
|
gi.ReflectInitSlice(dst, srcLen, srcLen)
|
||||||
|
for j := range srcLen {
|
||||||
|
err := Convert(src.Index(j), dst.Index(numValid), checkValidateTag)
|
||||||
|
if err != nil {
|
||||||
|
sliceErrs.Add(err.Subjectf("[%d]", j))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
numValid++
|
||||||
|
}
|
||||||
|
if err := sliceErrs.Error(); err != nil {
|
||||||
|
dst.SetLen(numValid) // shrink to number of elements that were successfully converted
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func ConvertString(src string, dst reflect.Value) (convertible bool, convErr gperr.Error) {
|
func ConvertString(src string, dst reflect.Value) (convertible bool, convErr gperr.Error) {
|
||||||
convertible = true
|
convertible = true
|
||||||
dstT := dst.Type()
|
dstT := dst.Type()
|
||||||
if dst.Kind() == reflect.Pointer {
|
if dst.Kind() == reflect.Pointer {
|
||||||
if dst.IsNil() {
|
if dst.IsNil() {
|
||||||
|
// Early return for empty string
|
||||||
|
if src == "" {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
initPtr(dst)
|
initPtr(dst)
|
||||||
}
|
}
|
||||||
dst = dst.Elem()
|
dst = dst.Elem()
|
||||||
dstT = dst.Type()
|
dstT = dst.Type()
|
||||||
}
|
}
|
||||||
if dst.Kind() == reflect.String {
|
|
||||||
dst.SetString(src)
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Early return for empty string
|
// Early return for empty string
|
||||||
if src == "" {
|
if src == "" {
|
||||||
@@ -501,6 +508,17 @@ func ConvertString(src string, dst reflect.Value) (convertible bool, convErr gpe
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if dst.Kind() == reflect.String {
|
||||||
|
dst.SetString(src)
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if (*T).Convertor is implemented
|
||||||
|
if addr := dst.Addr(); addr.Type().Implements(reflect.TypeFor[strutils.Parser]()) {
|
||||||
|
parser := addr.Interface().(strutils.Parser)
|
||||||
|
return true, gperr.Wrap(parser.Parse(src))
|
||||||
|
}
|
||||||
|
|
||||||
switch dstT {
|
switch dstT {
|
||||||
case reflect.TypeFor[time.Duration]():
|
case reflect.TypeFor[time.Duration]():
|
||||||
d, err := time.ParseDuration(src)
|
d, err := time.ParseDuration(src)
|
||||||
@@ -512,12 +530,6 @@ func ConvertString(src string, dst reflect.Value) (convertible bool, convErr gpe
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if (*T).Convertor is implemented
|
|
||||||
if dst.Addr().Type().Implements(parserType) {
|
|
||||||
parser := dst.Addr().Interface().(strutils.Parser)
|
|
||||||
return true, gperr.Wrap(parser.Parse(src))
|
|
||||||
}
|
|
||||||
|
|
||||||
if gi.ReflectIsNumeric(dst) || dst.Kind() == reflect.Bool {
|
if gi.ReflectIsNumeric(dst) || dst.Kind() == reflect.Bool {
|
||||||
err := gi.ReflectStrToNumBool(dst, src)
|
err := gi.ReflectStrToNumBool(dst, src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -527,29 +539,25 @@ func ConvertString(src string, dst reflect.Value) (convertible bool, convErr gpe
|
|||||||
}
|
}
|
||||||
|
|
||||||
// yaml like
|
// yaml like
|
||||||
var tmp any
|
|
||||||
switch dst.Kind() {
|
switch dst.Kind() {
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
// Avoid unnecessary TrimSpace if we can detect the format early
|
|
||||||
srcLen := len(src)
|
|
||||||
if srcLen == 0 {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// one liner is comma separated list
|
// one liner is comma separated list
|
||||||
isMultiline := strings.ContainsRune(src, '\n')
|
isMultiline := strings.IndexByte(src, '\n') != -1
|
||||||
if !isMultiline && src[0] != '-' {
|
if !isMultiline && src[0] != '-' && src[0] != '[' {
|
||||||
values := strutils.CommaSeperatedList(src)
|
values := strutils.CommaSeperatedList(src)
|
||||||
gi.ReflectInitSlice(dst, len(values), len(values))
|
size := len(values)
|
||||||
|
gi.ReflectInitSlice(dst, size, size)
|
||||||
var errs gperr.Builder
|
var errs gperr.Builder
|
||||||
for i, v := range values {
|
for i, v := range values {
|
||||||
_, err := ConvertString(v, dst.Index(i))
|
_, err := ConvertString(v, dst.Index(i))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs.Add(err.Subjectf("[%d]", i))
|
errs.AddSubjectf(err, "[%d]", i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err := errs.Error()
|
if errs.HasError() {
|
||||||
return true, err
|
return true, errs.Error()
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
sl := []any{}
|
sl := []any{}
|
||||||
@@ -557,18 +565,17 @@ func ConvertString(src string, dst reflect.Value) (convertible bool, convErr gpe
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return true, gperr.Wrap(err)
|
return true, gperr.Wrap(err)
|
||||||
}
|
}
|
||||||
tmp = sl
|
return true, ConvertSlice(reflect.ValueOf(sl), dst, true)
|
||||||
case reflect.Map, reflect.Struct:
|
case reflect.Map, reflect.Struct:
|
||||||
rawMap := SerializedObject{}
|
rawMap := SerializedObject{}
|
||||||
err := yaml.Unmarshal(unsafe.Slice(unsafe.StringData(src), len(src)), &rawMap)
|
err := yaml.Unmarshal(unsafe.Slice(unsafe.StringData(src), len(src)), &rawMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return true, gperr.Wrap(err)
|
return true, gperr.Wrap(err)
|
||||||
}
|
}
|
||||||
tmp = rawMap
|
return true, mapUnmarshalValidate(rawMap, dst, true)
|
||||||
default:
|
default:
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
return true, Convert(reflect.ValueOf(tmp), dst, true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var envRegex = regexp.MustCompile(`\$\{([^}]+)\}`) // e.g. ${CLOUDFLARE_API_KEY}
|
var envRegex = regexp.MustCompile(`\$\{([^}]+)\}`) // e.g. ${CLOUDFLARE_API_KEY}
|
||||||
|
|||||||
@@ -32,9 +32,9 @@ func BenchmarkDeserialize(b *testing.B) {
|
|||||||
"c": "1,2,3",
|
"c": "1,2,3",
|
||||||
"d": "a: a\nb: b\nc: c",
|
"d": "a: a\nb: b\nc: c",
|
||||||
"e": "- a: a\n b: b\n c: c",
|
"e": "- a: a\n b: b\n c: c",
|
||||||
"f": map[string]any{"a": "a", "b": "456", "c": []string{"1", "2", "3"}},
|
"f": map[string]any{"a": "a", "b": "456", "c": `1,2,3`},
|
||||||
"g": map[string]any{"g1": "1.23", "g2": 123},
|
"g": map[string]any{"g1": "1.23", "g2": 123},
|
||||||
"h": []map[string]any{{"a": 123, "b": "456", "c": []string{"1", "2", "3"}}},
|
"h": []map[string]any{{"a": 123, "b": "456", "c": `["1","2","3"]`}},
|
||||||
"j": "1.23",
|
"j": "1.23",
|
||||||
"k": 123,
|
"k": 123,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user