反射-恢复time.Time实例

I am developing a program in go that needs to store and retrieve an array of custom structure instances using the sessions package of the Gorilla toolkit. For restoring the custom structure I need to make use of reflection features. The issue is that my structure named Timestamp includes two time.Time instances and I have not been able to restore the instances. Thus, my question is how to restore a time.Time instance.

Below you can see my code for the Timespan structure as well as the code for storing and reading the Timespan array in the session store.

type Timespan struct {
    ID uint8;
    StartDate time.Time;
    EndDate time.Time;
}

func (server *WebServer) setTimespans(writer http.ResponseWriter, request *http.Request, timespans [model.TimespanCount]*model.Timespan) error {
    var session *sessions.Session;
    var sessionDecodingException error;
    session, sessionDecodingException = server.SessionStore.Get(request, authenticationSessionName);
    if sessionDecodingException != nil {
        return sessionDecodingException;
    }


    session.Values[sessionTimestamps] = timespans;
    return nil;
}

func (server *WebServer) getTimespans(request *http.Request) ([model.TimespanCount]*model.Timespan, error) {
    var session *sessions.Session;
    var sessionDecodingException error;
    session, sessionDecodingException = server.SessionStore.Get(request, authenticationSessionName);
    var readTimespans [model.TimespanCount]*model.Timespan;
    if sessionDecodingException != nil {
        return readTimespans, sessionDecodingException;
    }

    interfaceValue := reflect.ValueOf(session.Values[sessionTimestamps]);
    var actuallyAddedTimespan *model.Timespan;
    for counter := 0; counter < model.TimespanCount; counter++ {
        actuallyAddedTimespan = &model.Timespan{};
        actuallyReflectedTimespan := interfaceValue.Index(counter).Elem();
        actuallyAddedTimespan.ID = uint8(actuallyReflectedTimespan.FieldByName("ID").Uint());
        //actuallyAddedTimespan.StartDate = actuallyReflectedTimespan.FieldByName("StartDate");
        //actuallyAddedTimespan.EndDate = actuallyReflectedTimespan.FieldByName("EndDate");
        fmt.Println(actuallyAddedTimespan);
    }
    return readTimespans, nil;
}

You need to get the interface to the field:

actuallyAddedTimespan.StartDate = actuallyReflectedTimespan.FieldByName("StartDate").Interface().(time.Time)

playground

Personal opinion time, using reflection for this rather than using a simple interface is:

  1. Slow.
  2. Inefficient
  3. Can break easily if you change how your struct looks and forget to update the reflection code.

Example of using an interface:

func main() {
    ts := &Timespan{ID: 102, StartDate: time.Now().AddDate(6, 0, 0), EndDate: time.Now().AddDate(8, 0, 0)}
    m := map[string]interface{}{
        "key": ts,
    }
    switch v := m["key"].(type) {
    case Timespaner:
        fmt.Println(v.Value())
    default:
        fmt.Println("wtfmate?")
    }
}

func (ts *Timespan) Value() (id uint8, start, end time.Time) {
    return ts.ID, ts.StartDate, ts.EndDate
}

type Timespaner interface {
    Value() (id uint8, start, end time.Time)
}

playground

You can use the Interface() function to get the interface{} value from a struct. Then, you can use a type assertion to get the correct type back:

func main() {
    t := &Timespan{42, time.Now(), time.Now()}
    reflectedPointer := reflect.ValueOf(t)
    reflectedTimespan := reflectedPointer.Elem()
    var timespan Timespan = reflectedTimespan.Interface().(Timespan)
    fmt.Println(*t)
    fmt.Println(timespan)
}