13 // typedDecodeHook takes a raw DecodeHookFunc (an interface{}) and turns
14 // it into the proper DecodeHookFunc type, such as DecodeHookFuncType.
15 func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc {
16 // Create variables here so we can reference them with the reflect pkg
17 var f1 DecodeHookFuncType
18 var f2 DecodeHookFuncKind
20 // Fill in the variables into this interface and the rest is done
21 // automatically using the reflect package.
22 potential := []interface{}{f1, f2}
24 v := reflect.ValueOf(h)
26 for _, raw := range potential {
27 pt := reflect.ValueOf(raw).Type()
28 if vt.ConvertibleTo(pt) {
29 return v.Convert(pt).Interface()
36 // DecodeHookExec executes the given decode hook. This should be used
37 // since it'll naturally degrade to the older backwards compatible DecodeHookFunc
38 // that took reflect.Kind instead of reflect.Type.
41 from reflect.Type, to reflect.Type,
42 data interface{}) (interface{}, error) {
43 switch f := typedDecodeHook(raw).(type) {
44 case DecodeHookFuncType:
45 return f(from, to, data)
46 case DecodeHookFuncKind:
47 return f(from.Kind(), to.Kind(), data)
49 return nil, errors.New("invalid decode hook signature")
53 // ComposeDecodeHookFunc creates a single DecodeHookFunc that
54 // automatically composes multiple DecodeHookFuncs.
56 // The composed funcs are called in order, with the result of the
57 // previous transformation.
58 func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc {
62 data interface{}) (interface{}, error) {
64 for _, f1 := range fs {
65 data, err = DecodeHookExec(f1, f, t, data)
70 // Modify the from kind to be correct with the new data
72 if val := reflect.ValueOf(data); val.IsValid() {
81 // StringToSliceHookFunc returns a DecodeHookFunc that converts
82 // string to []string by splitting on the given sep.
83 func StringToSliceHookFunc(sep string) DecodeHookFunc {
87 data interface{}) (interface{}, error) {
88 if f != reflect.String || t != reflect.Slice {
94 return []string{}, nil
97 return strings.Split(raw, sep), nil
101 // StringToTimeDurationHookFunc returns a DecodeHookFunc that converts
102 // strings to time.Duration.
103 func StringToTimeDurationHookFunc() DecodeHookFunc {
107 data interface{}) (interface{}, error) {
108 if f.Kind() != reflect.String {
111 if t != reflect.TypeOf(time.Duration(5)) {
115 // Convert it by parsing
116 return time.ParseDuration(data.(string))
120 // StringToIPHookFunc returns a DecodeHookFunc that converts
122 func StringToIPHookFunc() DecodeHookFunc {
126 data interface{}) (interface{}, error) {
127 if f.Kind() != reflect.String {
130 if t != reflect.TypeOf(net.IP{}) {
134 // Convert it by parsing
135 ip := net.ParseIP(data.(string))
137 return net.IP{}, fmt.Errorf("failed parsing ip %v", data)
144 // StringToIPNetHookFunc returns a DecodeHookFunc that converts
145 // strings to net.IPNet
146 func StringToIPNetHookFunc() DecodeHookFunc {
150 data interface{}) (interface{}, error) {
151 if f.Kind() != reflect.String {
154 if t != reflect.TypeOf(net.IPNet{}) {
158 // Convert it by parsing
159 _, net, err := net.ParseCIDR(data.(string))
164 // StringToTimeHookFunc returns a DecodeHookFunc that converts
165 // strings to time.Time.
166 func StringToTimeHookFunc(layout string) DecodeHookFunc {
170 data interface{}) (interface{}, error) {
171 if f.Kind() != reflect.String {
174 if t != reflect.TypeOf(time.Time{}) {
178 // Convert it by parsing
179 return time.Parse(layout, data.(string))
183 // WeaklyTypedHook is a DecodeHookFunc which adds support for weak typing to
186 // Note that this is significantly different from the WeaklyTypedInput option
187 // of the DecoderConfig.
188 func WeaklyTypedHook(
191 data interface{}) (interface{}, error) {
192 dataVal := reflect.ValueOf(data)
201 case reflect.Float32:
202 return strconv.FormatFloat(dataVal.Float(), 'f', -1, 64), nil
204 return strconv.FormatInt(dataVal.Int(), 10), nil
206 dataType := dataVal.Type()
207 elemKind := dataType.Elem().Kind()
208 if elemKind == reflect.Uint8 {
209 return string(dataVal.Interface().([]uint8)), nil
212 return strconv.FormatUint(dataVal.Uint(), 10), nil