form icon indicating copy to clipboard operation
form copied to clipboard

Encode and Decode custom type function, we can get match `reflect.Type` in callback!

Open thinkgos opened this issue 1 year ago • 0 comments

Package version eg. v4:

Issue, Question or Enhancement:

DecodeCustomTypeFunc should be not func([]string) (interface{}, error), may be func(reflect.Type,[]string) (interface{}, error) will be better. also. EncodeCustomTypeFunc: func(x interface{}) ([]string, error) -> func(t reflect.Type, x interface{}) ([]string, error).

get the match reflect.Type. the custom type parse function can do more thing.

Code sample, to showcase or reproduce:

example below:

I want to parser id=1,2,3 to []int or []CustomInt or more.

type CustomInt int

func DecodeCustomIntSlice[T ~int](values []string) (any, error) {
	if len(values) == 0 {
		return []T{}, nil
	}
	// FIXME: make slice space
	ret := make([]T, 0)
	for _, s := range values {
		for _, v := range strings.Split(s, ",") {
			i, err := strconv.ParseInt(v, 10, 0)
			if err != nil {
				return nil, err
			}
			ret = append(ret, T(i))
		}
	}
	return ret, nil
}
dec := form.NewDecoder()
dec.SetTagName("json")
dec.RegisterCustomTypeFunc(DecodeCustomIntSlice[int], []int{})
dec.RegisterCustomTypeFunc(DecodeCustomIntSlice[CustomInt], []CustomInt{})

I have to register twice. if decoder.go#L191 pass the reflect.Type to custom type parser function, we can register in one line.

if DecodeCustomTypeFunc is func(reflect.Type,[]string) (interface{}, error), we can simple do below:

type CustomInt int
type CustomInt1 int
type CustomInt2 int

func DecodeCommaStringToSlice(t reflect.Type, values []string) (any, error) {
	if t.Kind() != reflect.Slice {
		return nil, &form.InvalidDecoderError{Type: t}
	}
	ret := reflect.MakeSlice(t, 0, 0)
	te := t.Elem()
	teKind := te.Kind()
	for _, s := range values {
		for _, ss := range strings.Split(s, ",") {
			switch teKind {
			case reflect.Int:
				i, err := strconv.ParseInt(ss, 10, 0)
				if err != nil {
					return nil, err
				}
				val := reflect.New(te).Elem()
				val.SetInt(i)
				ret = reflect.Append(ret, val)
			}
		}
	}
	return ret.Interface(), nil
}

dec := form.NewDecoder()
dec.SetTagName("json")
dec.RegisterCustomTypeFunc(DecodeCommaStringToSlice, []int{},[]CustomInt{},[]CustomInt1{},[]CustomInt2{})

Is there any other better solution?

thinkgos avatar May 29 '24 01:05 thinkgos