-
Notifications
You must be signed in to change notification settings - Fork 13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Decode: time.Time field of input struct decoded to the zero value of time.Time in output struct #20
Comments
I'd like to see the default behaviour here changed also, using the standard RFC3339 or handling time stuct mapping would be ideal |
+1 for this |
Are you asking for a decode hook that converts a string that contains a RFC3339 timestamp to |
@sagikazarmark I can't speak for everyone, but it would be nice if mapstructure decoded time.Time correctly by default, i.e. no additional config. Most efficient implementation, up to you :) |
Not the prettiest code, but atleast it works now .. the non-typed nil return took me a while to figure out package main
import (
"database/sql"
"fmt"
"github.com/thisisdevelopment/go-dockly/xerrors/iferr"
"reflect"
"time"
"github.com/go-viper/mapstructure/v2"
"gorm.io/gorm"
)
func decodeTime(mapData map[string]interface{}) (*time.Time, error) {
to := &time.Time{}
json, ok := mapData["_json"]
if !ok {
return nil, nil //TODO: error
}
err := to.UnmarshalJSON(json.([]byte))
if to.IsZero() {
return nil, nil
}
return to, err
}
func encodeTime(data *time.Time) (map[string]interface{}, error) {
if data == nil {
return map[string]interface{}{"_json": []uint8("null")}, nil
}
json, err := data.MarshalJSON()
return map[string]interface{}{"_json": json}, err
}
func main() {
toTime := func() mapstructure.DecodeHookFunc {
timePtrType := reflect.TypeOf(&time.Time{})
timeType := reflect.TypeOf(time.Time{})
strMapType := reflect.TypeOf(map[string]interface{}{})
sqlNullTimeType := reflect.TypeOf(sql.NullTime{})
sqlNullTimePtrType := reflect.TypeOf(&sql.NullTime{})
gormDeletedAtType := reflect.TypeOf(gorm.DeletedAt{})
gormDeletedAtPtrType := reflect.TypeOf(&gorm.DeletedAt{})
return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) {
//for struct => struct conversions mapstructure currently employs the tactic to always decode it to a map
//and then convert the map to the new struct. For the time.Time struct this fails as the time struct has no
//public fields.
//To work around this we json encode/decode the time
//We also support sql.NullTime and gorm.DeletedAt
if t == strMapType {
if f == timePtrType {
return encodeTime(data.(*time.Time))
} else if f == timeType {
d := data.(time.Time)
return encodeTime(&d)
} else if f == sqlNullTimeType {
d := data.(sql.NullTime)
if d.Valid {
return encodeTime(&d.Time)
} else {
return encodeTime(nil)
}
} else if f == gormDeletedAtType {
d := data.(gorm.DeletedAt)
if d.Valid {
return encodeTime(&d.Time)
} else {
return encodeTime(nil)
}
} else if f == sqlNullTimePtrType {
d := data.(*sql.NullTime)
if d.Valid {
return encodeTime(&d.Time)
} else {
return encodeTime(nil)
}
} else if f == gormDeletedAtPtrType {
d := data.(*gorm.DeletedAt)
if d.Valid {
return encodeTime(&d.Time)
} else {
return encodeTime(nil)
}
}
}
if f == strMapType && (t == sqlNullTimeType || t == sqlNullTimePtrType || t == timeType || t == timePtrType) {
to, err := decodeTime(data.(map[string]interface{}))
if err != nil {
return nil, err
}
if t == timeType {
if to == nil {
//as we cannot return nil, return zero time instead
return time.Time{}, nil
}
return *to, nil
} else if t == timePtrType {
if to == nil {
//we can't just return to as to is a typed nil; currently mapstructure does not handle that well
return nil, nil
} else {
return to, nil
}
} else if t == sqlNullTimeType {
if to == nil {
return sql.NullTime{Time: time.Time{}, Valid: false}, nil
} else {
return sql.NullTime{Time: *to, Valid: true}, nil
}
} else if t == sqlNullTimePtrType {
if to == nil {
return &sql.NullTime{Time: time.Time{}, Valid: false}, nil
} else {
return &sql.NullTime{Time: *to, Valid: true}, nil
}
}
}
return data, nil
}
}
type From struct {
Name string
CreatedAt time.Time
DeletedAt gorm.DeletedAt
}
type To struct {
Name string
CreatedAt time.Time
DeletedAt *time.Time
}
input := &From{Name: "Test", CreatedAt: time.Now()}
result := new(To)
var decoder, _ = mapstructure.NewDecoder(&mapstructure.DecoderConfig{Squash: true, DecodeHook: toTime(), Result: result})
err := decoder.Decode(input)
iferr.Exit(err)
fmt.Printf("%+v\n", result)
} |
I have the same problem. I tried some hook functions, but it still did not work |
Repost of The time.Time type is converted to empty map from
github.com/mitchellh/mapstructure
.When using
Decode
, the fields of typetime.Time
from the input struct are being decoded to the zero value oftime.Time
,time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)
, in the output struct.The text was updated successfully, but these errors were encountered: