Open kaysonwu opened 1 year ago
I defined an alias type DateTime for time.Time, It mainly implements the time.DateTime format string and time.Time interchange
DateTime
time.Time
time.DateTime
string
type DateTime time.Time func (d DateTime) MarshalJSON() ([]byte, error) { t := time.Time(d) if t.Equal(time.Time{}) { return []byte("null"), nil } return []byte(`"` + t.Format(time.DateTime) + `"`), nil } func (d *DateTime) UnmarshalJSON(data []byte) error { str := string(data) if str == "null" { *d = DateTime(time.Time{}) } else if date, err := time.Parse(time.DateTime, str); err == nil { *d = DateTime(date) } else { return err } return nil } func (d DateTime) Value() (driver.Value, error) { t := time.Time(d) if t.Equal(time.Time{}) { return nil, nil } return t.Format(time.DateTime), nil } func (d *DateTime) Scan(value any) error { if val, ok := value.(time.Time); ok { *d = DateTime(val) return nil } return fmt.Errorf("Failed to scan type %T into DateTime", value) }
Next, I will apply DateTime to the user model and provide a NewModel factory function
type User struct { Name string `json:"name" gorm:"size:255"` CreatedAt DateTime `json:"created_at"` UpdatedAt DateTime `json:"created_at"` } func NewModel[T any](attributes map[string]any, tagName string) (T, error) { var model T decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ DecodeHook: mapstructure.StringToTimeHookFunc(time.DateTime), WeaklyTypedInput: true, TagName: tagName, Result: &model, }) if err != nil { return model, err } return model, decoder.Decode(attributes) }
When I run, I receive 'created_at' expected a map, got 'string' error message.
'created_at' expected a map, got 'string'
attributes := map[string]any { "name": "foo", "created_at": "2006-01-02 15:04:05", "updated_at": "2006-01-02 15:04:05", } user, err := NewModel[User](attributes) if err != nil { fmt.Println(err) } else { fmt.Println("ok") }
Tracking the code, I found that it can enhance StringToTimeHookFunc to make it more adaptable, e.g.
StringToTimeHookFunc
func StringToTimeHookFunc(layout string) mapstructure.DecodeHookFunc { return func( f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { if f.Kind() != reflect.String { return data, nil } // Rewrite t != reflect.TypeOf(time.Time{}) to ConvertibleTo if !t.ConvertibleTo(reflect.TypeOf(time.Time{})) { return data, nil } // Convert it by parsing value, err := time.Parse(layout, data.(string)) if err != nil { return data, err } return reflect.ValueOf(value).Convert(t).Interface(), nil } }
I defined an alias type
DateTime
fortime.Time
, It mainly implements thetime.DateTime
formatstring
andtime.Time
interchangeNext, I will apply
DateTime
to the user model and provide a NewModel factory functionWhen I run, I receive
'created_at' expected a map, got 'string'
error message.Tracking the code, I found that it can enhance
StringToTimeHookFunc
to make it more adaptable, e.g.