11 // typedDecodeHook takes a raw DecodeHookFunc (an interface{}) and turns
12 // it into the proper DecodeHookFunc type, such as DecodeHookFuncType.
13 func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc {
14 // Create variables here so we can reference them with the reflect pkg
15 var f1 DecodeHookFuncType
16 var f2 DecodeHookFuncKind
18 // Fill in the variables into this interface and the rest is done
19 // automatically using the reflect package.
20 potential := []interface{}{f1, f2}
22 v := reflect.ValueOf(h)
24 for _, raw := range potential {
25 pt := reflect.ValueOf(raw).Type()
26 if vt.ConvertibleTo(pt) {
27 return v.Convert(pt).Interface()
34 // DecodeHookExec executes the given decode hook. This should be used
35 // since it'll naturally degrade to the older backwards compatible DecodeHookFunc
36 // that took reflect.Kind instead of reflect.Type.
39 from reflect.Type, to reflect.Type,
40 data interface{}) (interface{}, error) {
41 switch f := typedDecodeHook(raw).(type) {
42 case DecodeHookFuncType:
43 return f(from, to, data)
44 case DecodeHookFuncKind:
45 return f(from.Kind(), to.Kind(), data)
47 return nil, errors.New("invalid decode hook signature")
51 // ComposeDecodeHookFunc creates a single DecodeHookFunc that
52 // automatically composes multiple DecodeHookFuncs.
54 // The composed funcs are called in order, with the result of the
55 // previous transformation.
56 func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc {
60 data interface{}) (interface{}, error) {
62 for _, f1 := range fs {
63 data, err = DecodeHookExec(f1, f, t, data)
68 // Modify the from kind to be correct with the new data
70 if val := reflect.ValueOf(data); val.IsValid() {
79 // StringToSliceHookFunc returns a DecodeHookFunc that converts
80 // string to []string by splitting on the given sep.
81 func StringToSliceHookFunc(sep string) DecodeHookFunc {
85 data interface{}) (interface{}, error) {
86 if f != reflect.String || t != reflect.Slice {
92 return []string{}, nil
95 return strings.Split(raw, sep), nil
99 // StringToTimeDurationHookFunc returns a DecodeHookFunc that converts
100 // strings to time.Duration.
101 func StringToTimeDurationHookFunc() DecodeHookFunc {
105 data interface{}) (interface{}, error) {
106 if f.Kind() != reflect.String {
109 if t != reflect.TypeOf(time.Duration(5)) {
113 // Convert it by parsing
114 return time.ParseDuration(data.(string))
118 // WeaklyTypedHook is a DecodeHookFunc which adds support for weak typing to
121 // Note that this is significantly different from the WeaklyTypedInput option
122 // of the DecoderConfig.
123 func WeaklyTypedHook(
126 data interface{}) (interface{}, error) {
127 dataVal := reflect.ValueOf(data)
136 case reflect.Float32:
137 return strconv.FormatFloat(dataVal.Float(), 'f', -1, 64), nil
139 return strconv.FormatInt(dataVal.Int(), 10), nil
141 dataType := dataVal.Type()
142 elemKind := dataType.Elem().Kind()
143 if elemKind == reflect.Uint8 {
144 return string(dataVal.Interface().([]uint8)), nil
147 return strconv.FormatUint(dataVal.Uint(), 10), nil