bindData will bind data ONLY fields in destination struct that have EXPLICIT tag
(destination any, data map[string][]string, tag string, dataFiles map[string][]*multipart.FileHeader)
| 206 | |
| 207 | // bindData will bind data ONLY fields in destination struct that have EXPLICIT tag |
| 208 | func bindData(destination any, data map[string][]string, tag string, dataFiles map[string][]*multipart.FileHeader) error { |
| 209 | if destination == nil || (len(data) == 0 && len(dataFiles) == 0) { |
| 210 | return nil |
| 211 | } |
| 212 | hasFiles := len(dataFiles) > 0 |
| 213 | typ := reflect.TypeOf(destination).Elem() |
| 214 | val := reflect.ValueOf(destination).Elem() |
| 215 | |
| 216 | // Support binding to limited Map destinations: |
| 217 | // - map[string][]string, |
| 218 | // - map[string]string <-- (binds first value from data slice) |
| 219 | // - map[string]any |
| 220 | // You are better off binding to struct but there are user who want this map feature. Source of data for these cases are: |
| 221 | // params,query,header,form as these sources produce string values, most of the time slice of strings, actually. |
| 222 | if typ.Kind() == reflect.Map && typ.Key().Kind() == reflect.String { |
| 223 | k := typ.Elem().Kind() |
| 224 | isElemInterface := k == reflect.Interface |
| 225 | isElemString := k == reflect.String |
| 226 | isElemSliceOfStrings := k == reflect.Slice && typ.Elem().Elem().Kind() == reflect.String |
| 227 | if !isElemSliceOfStrings && !isElemString && !isElemInterface { |
| 228 | return nil |
| 229 | } |
| 230 | if val.IsNil() { |
| 231 | val.Set(reflect.MakeMap(typ)) |
| 232 | } |
| 233 | for k, v := range data { |
| 234 | if isElemString { |
| 235 | val.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(v[0])) |
| 236 | } else if isElemInterface { |
| 237 | // To maintain backward compatibility, we always bind to the first string value |
| 238 | // and not the slice of strings when dealing with map[string]any{} |
| 239 | val.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(v[0])) |
| 240 | } else { |
| 241 | val.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(v)) |
| 242 | } |
| 243 | } |
| 244 | return nil |
| 245 | } |
| 246 | |
| 247 | // !struct |
| 248 | if typ.Kind() != reflect.Struct { |
| 249 | if tag == "param" || tag == "query" || tag == "header" { |
| 250 | // incompatible type, data is probably to be found in the body |
| 251 | return nil |
| 252 | } |
| 253 | return errors.New("binding element must be a struct") |
| 254 | } |
| 255 | |
| 256 | meta := bindMetaFor(typ) |
| 257 | for fi := range meta.fields { // iterate over all destination fields |
| 258 | fm := &meta.fields[fi] |
| 259 | structField := val.Field(fm.index) |
| 260 | if fm.anonymous { |
| 261 | if structField.Kind() == reflect.Pointer { |
| 262 | structField = structField.Elem() |
| 263 | } |
| 264 | } |
| 265 | if !structField.CanSet() { |
searching dependent graphs…