github.com/egonelbre/exp@v0.0.0-20240430123955-ed1d3aa93911/fields/02_reflect/scan.go (about) 1 package main 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "reflect" 8 "strings" 9 ) 10 11 //gistsnip:start:unmarshal 12 func Unmarshal(rd io.Reader, data interface{}) error { 13 var config jsonConfig 14 err := json.NewDecoder(rd).Decode(&config) 15 if err != nil { 16 return err 17 } 18 19 return config.Scan(data) 20 } 21 22 type jsonConfig struct { 23 Fields []jsonField 24 } 25 26 type jsonField struct { 27 Name string 28 Type string 29 Val interface{} 30 Multiplier interface{} 31 } 32 33 //gistsnip:end:unmarshal 34 35 //gistsnip:start:scan 36 func (config *jsonConfig) Scan(r interface{}) error { 37 // check that r is a pointer to some struct 38 rv := reflect.ValueOf(r) 39 if rv.Kind() != reflect.Ptr || rv.Elem().Kind() != reflect.Struct { 40 return fmt.Errorf("expected pointer to a struct, got %T", r) 41 } 42 43 s := rv.Elem() 44 t := s.Type() 45 46 // iterate over all struct fields 47 for i, n := 0, s.NumField(); i < n; i++ { 48 resultField := s.Field(i) 49 50 // find the corresponding field from config 51 field, err := config.findField(t.Field(i).Name) 52 if err != nil { 53 return err 54 } 55 56 // assign field value to the struct field 57 err = config.assignField(field, resultField.Addr().Interface()) 58 if err != nil { 59 return err 60 } 61 } 62 63 return nil 64 } 65 66 //gistsnip:end:scan 67 68 func (config *jsonConfig) findField(name string) (*jsonField, error) { 69 for i := 0; i < len(config.Fields); i++ { 70 field := &config.Fields[i] 71 if strings.EqualFold(field.Name, name) { 72 return field, nil 73 } 74 } 75 return nil, fmt.Errorf("unable to find field " + name) 76 } 77 78 //gistsnip:start:assignField 79 func (config *jsonConfig) assignField(field *jsonField, p interface{}) error { 80 // p is a pointer to struct field 81 switch p := p.(type) { 82 case *uint: 83 uv, ok := field.Val.(float64) 84 if !ok || field.Type != "uint" { 85 return fmt.Errorf("expected uint, got %T and %v", field.Val, field.Type) 86 } 87 *p = uint(uv) 88 case *float64: 89 uv, ok := field.Val.(float64) 90 if !ok || field.Type != "float" { 91 return fmt.Errorf("expected float, got %T and %v", field.Val, field.Type) 92 } 93 *p = uv 94 default: 95 return fmt.Errorf("unhandled field type %T", p) 96 } 97 return nil 98 } 99 100 //gistsnip:end:assignField