mirror of
https://github.com/yusing/godoxy.git
synced 2026-01-11 22:30:47 +01: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)
|
||||
case srcKind == reflect.Slice: // slice to slice
|
||||
return ConvertSlice(src, dst, checkValidateTag)
|
||||
}
|
||||
|
||||
return ErrUnsupportedConversion.Subjectf("%s to %s", srcT, dstT)
|
||||
}
|
||||
|
||||
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 dstT.Kind() != reflect.Slice {
|
||||
return ErrUnsupportedConversion.Subject(dstT.String() + " to " + srcT.String())
|
||||
if dst.Kind() != reflect.Slice {
|
||||
return ErrUnsupportedConversion.Subjectf("%s to %s", dst.Type(), src.Type())
|
||||
}
|
||||
|
||||
var sliceErrs gperr.Builder
|
||||
i := 0
|
||||
numValid := 0
|
||||
gi.ReflectInitSlice(dst, srcLen, srcLen)
|
||||
for j, v := range src.Seq2() {
|
||||
err := Convert(v, dst.Index(i), checkValidateTag)
|
||||
for j := range srcLen {
|
||||
err := Convert(src.Index(j), dst.Index(numValid), checkValidateTag)
|
||||
if err != nil {
|
||||
sliceErrs.Add(err.Subjectf("[%d]", j))
|
||||
continue
|
||||
}
|
||||
i++
|
||||
numValid++
|
||||
}
|
||||
if err := sliceErrs.Error(); err != nil {
|
||||
dst.SetLen(i) // shrink to number of elements that were successfully converted
|
||||
dst.SetLen(numValid) // shrink to number of elements that were successfully converted
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return ErrUnsupportedConversion.Subjectf("%s to %s", srcT.String(), dstT.String())
|
||||
}
|
||||
|
||||
var parserType = reflect.TypeFor[strutils.Parser]()
|
||||
|
||||
func ConvertString(src string, dst reflect.Value) (convertible bool, convErr gperr.Error) {
|
||||
convertible = true
|
||||
dstT := dst.Type()
|
||||
if dst.Kind() == reflect.Pointer {
|
||||
if dst.IsNil() {
|
||||
// Early return for empty string
|
||||
if src == "" {
|
||||
return true, nil
|
||||
}
|
||||
initPtr(dst)
|
||||
}
|
||||
dst = dst.Elem()
|
||||
dstT = dst.Type()
|
||||
}
|
||||
if dst.Kind() == reflect.String {
|
||||
dst.SetString(src)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Early return for empty string
|
||||
if src == "" {
|
||||
@@ -501,6 +508,17 @@ func ConvertString(src string, dst reflect.Value) (convertible bool, convErr gpe
|
||||
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 {
|
||||
case reflect.TypeFor[time.Duration]():
|
||||
d, err := time.ParseDuration(src)
|
||||
@@ -512,12 +530,6 @@ func ConvertString(src string, dst reflect.Value) (convertible bool, convErr gpe
|
||||
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 {
|
||||
err := gi.ReflectStrToNumBool(dst, src)
|
||||
if err != nil {
|
||||
@@ -527,29 +539,25 @@ func ConvertString(src string, dst reflect.Value) (convertible bool, convErr gpe
|
||||
}
|
||||
|
||||
// yaml like
|
||||
var tmp any
|
||||
switch dst.Kind() {
|
||||
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
|
||||
isMultiline := strings.ContainsRune(src, '\n')
|
||||
if !isMultiline && src[0] != '-' {
|
||||
isMultiline := strings.IndexByte(src, '\n') != -1
|
||||
if !isMultiline && src[0] != '-' && src[0] != '[' {
|
||||
values := strutils.CommaSeperatedList(src)
|
||||
gi.ReflectInitSlice(dst, len(values), len(values))
|
||||
size := len(values)
|
||||
gi.ReflectInitSlice(dst, size, size)
|
||||
var errs gperr.Builder
|
||||
for i, v := range values {
|
||||
_, err := ConvertString(v, dst.Index(i))
|
||||
if err != nil {
|
||||
errs.Add(err.Subjectf("[%d]", i))
|
||||
errs.AddSubjectf(err, "[%d]", i)
|
||||
}
|
||||
}
|
||||
err := errs.Error()
|
||||
return true, err
|
||||
if errs.HasError() {
|
||||
return true, errs.Error()
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
sl := []any{}
|
||||
@@ -557,18 +565,17 @@ func ConvertString(src string, dst reflect.Value) (convertible bool, convErr gpe
|
||||
if err != nil {
|
||||
return true, gperr.Wrap(err)
|
||||
}
|
||||
tmp = sl
|
||||
return true, ConvertSlice(reflect.ValueOf(sl), dst, true)
|
||||
case reflect.Map, reflect.Struct:
|
||||
rawMap := SerializedObject{}
|
||||
err := yaml.Unmarshal(unsafe.Slice(unsafe.StringData(src), len(src)), &rawMap)
|
||||
if err != nil {
|
||||
return true, gperr.Wrap(err)
|
||||
}
|
||||
tmp = rawMap
|
||||
return true, mapUnmarshalValidate(rawMap, dst, true)
|
||||
default:
|
||||
return false, nil
|
||||
}
|
||||
return true, Convert(reflect.ValueOf(tmp), dst, true)
|
||||
}
|
||||
|
||||
var envRegex = regexp.MustCompile(`\$\{([^}]+)\}`) // e.g. ${CLOUDFLARE_API_KEY}
|
||||
|
||||
@@ -32,9 +32,9 @@ func BenchmarkDeserialize(b *testing.B) {
|
||||
"c": "1,2,3",
|
||||
"d": "a: a\nb: b\nc: 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},
|
||||
"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",
|
||||
"k": 123,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user