In a if condition I'm trying to know if my data's type is time.Time.
What is the best way to get the res.Datas[i]'s data type and check it in a if loop ?
Assuming type of res.Datas[i] is not a concrete type but an interface type (e.g. interface{}), simply use a type assertion for this:
if t, ok := res.Datas[i].(time.Time); ok {
// it is of type time.Time
// t is of type time.Time, you can use it so
} else {
// not of type time.Time, or it is nil
}
If you don't need the time.Time value, you just want to tell if the interface value wraps a time.Time:
if _, ok := res.Datas[i].(time.Time); ok {
// it is of type time.Time
} else {
// not of type time.Time, or it is nil
}
Also note that the types time.Time and *time.Time are different. If a pointer to time.Time is wrapped, you need to check that as a different type.
Related
I have the following struct in one module:
type Event struct {
Name string `json:"name"`
Version string `json:"version"`
Payload interface{} `json:"payload"`
}
the goal is, that I can accept arbitrary structs as Payload and send the final structs of type Event as json-serialized strings using json.Marshal with some messaging-service.
However, when I try to json.Unmarshal in another project, I can't access the fields inside of Event.Payload, because Go obviously doesn't know about its final type.
I tried to use something like this:
type EventPayload struct{
Name string `json:"name"`
}
// ...
event := &events.Event{}
event.Payload = &EventPayload{}
if err := json.Unmarshal(msg.Data, event); err != nil {
return err
}
event.Payload.Name // Won't work: "type interface{} has no field or method Status"
however, Go still thinks, that event.Payload is an interface{}.
How can I tell Go the correct struct-type in this case?
You can do exactly what you're describing, you just need to use a type assertion to get the underlying type back. json.Unmarshal, provided an interface{} field with a concrete type prepopulated, will use that concrete type.
event := &Event{}
event.Payload = &EventPayload{}
msg := []byte(`{"Name": "foo", "Version": "1", "Payload": {"Name": "bar"}}`)
if err := json.Unmarshal(msg, event); err != nil {
panic(err)
}
pl := event.Payload.(*EventPayload)
fmt.Println(pl.Name)
Working example (slightly modified to run in playground): https://play.golang.org/p/IDXLKeMGw8_1
How can I tell Go the correct struct-type in this case?
You cannot. This is not how encoding/json.Unmarshal works. You either have to unmarshal into a proper type or something else or use json.Raw message and do a second unmarshaling or whatnot but they way you approached it simply doesn't work.
I'm unmarshalling a GET request from a JSON API. Soem of the fields are a Unix timestamp (in ms). For those fields, I wrote a method to handle unmarshalling. For that to work, I needed to create a new local type Datetime.
type Datetime time.Time
type Response struct {
Status string `json:"status"`
CreatedAt Datetime `json:"created_at"`
}
// Handles unmarshalling of Time from the API
func (dt *Datetime) UnmarshalJSON(b []byte) error {
var unixTimeMs int64
if err := json.Unmarshal(b, &unixTimeMs); err != nil {
return err
}
*dt = Datetime(time.Unix(0, unixTimeMs*1000000))
return nil
}
Now that I have this new Response object, I'd like to call time.Time methods on the Datetime. I've tried casting the object (i.e. response.CreatedAt.(time.Time)), but that leads to a panic.
How can I call methods like time.Time#string on Datetime?
Convert the Datetime to time.Time to call a Time method. Example:
fmt.Println(time.Time(dt).String())
Another approach is use a struct with an embedded time field:
type Datetime struct {
time.Time
}
All time.Time methods are promoted to the Datetime type. Modify the UnmarshalJSON method to set dt.Time instead of *dt.
The following works fine:
type MyStruct struct {
MyField int32
}
func SetReflectConcrete(obj *MyStruct, fieldName string, newValue interface{}) {
objElem := reflect.ValueOf(obj).Elem()
field := objElem.FieldByName(fieldName)
field.Set(reflect.ValueOf(newValue))
}
func main() {
myStruct := MyStruct{123}
SetReflectConcrete(myStruct, "MyField", int32{1234})
}
How can I make a variant of the SetReflect function that works on any struct? All my attempts so far have failed. The signature would be something like this:
func SetReflectInterface(obj interface{}, fieldName string, newValue interface{})
And is this even possible, when calling it like
SetReflectInterface(myStruct, "MyField", int32{1234})
or would it have to be called like
SetReflectInterface(&myStruct, "MyField", int32{1234})
(After all, interface{} has a pointer to the struct.)
Declare the argument as type interface{} as you noted. Pass a pointer to the struct as in the last code snippet.
func SetReflectConcrete(obj interface{}, fieldName string, newValue interface{}) {
objElem := reflect.ValueOf(obj).Elem()
field := objElem.FieldByName(fieldName)
field.Set(reflect.ValueOf(newValue))
}
myStruct := MyStruct{123}
SetReflectConcrete(&myStruct, "MyField", int32(1234))
Run it on the playground.
The reflect value must be addressable to set a field. The value will not be addressable if created directly from a struct.
I am trying to use Google Datastore to store data by Go. Since the EndDate is optional field, and don't want to store zero value in that field. If I make a pointer for time field, Google Datastore will send an error message - datastore: unsupported struct field type: *time.Time
How can I ignore zero value field in struct?
type Event struct {
StartDate time.Time `datastore:"start_date,noindex" json:"startDate"`
EndDate time.Time `datastore:"end_date,noindex" json:"endDate"`
}
The default saving mechanism does not handle optional fields. A field is either saved all the time, or never. There is no such thing as "only save if it's value does not equal to something".
The "optionally saved property" is considered a custom behavior, a custom saving mechanism, and as such, it has to be implemented manually. Go's way to do this is to implement the PropertyLoadSaver interface on your struct. Here I present 2 different methods to achieve that:
Manually saving fields
Here is an example how to do it by manually saving the fields (and excluding EndDate if it is the zero value):
type Event struct {
StartDate time.Time `datastore:"start_date,noindex" json:"startDate"`
EndDate time.Time `datastore:"end_date,noindex" json:"endDate"`
}
func (e *Event) Save(c chan<- datastore.Property) error {
defer close(c)
// Always save StartDate:
c <- datastore.Property{Name:"start_date", Value:e.StartDate, NoIndex: true}
// Only save EndDate if not zero value:
if !e.EndDate.IsZero() {
c <- datastore.Property{Name:"end_date", Value:e.EndDate, NoIndex: true}
}
return nil
}
func (e *Event) Load(c chan<- datastore.Property) error {
// No change required in loading, call default implementation:
return datastore.LoadStruct(e, c)
}
With another struct
Here's another way using another struct. The Load() implementation is always the same, only Save() differs:
func (e *Event) Save(c chan<- datastore.Property) error {
if !e.EndDate.IsZero() {
// If EndDate is not zero, save as usual:
return datastore.SaveStruct(e, c)
}
// Else we need a struct without the EndDate field:
s := struct{ StartDate time.Time `datastore:"start_date,noindex"` }{e.StartDate}
// Which now can be saved using the default saving mechanism:
return datastore.SaveStruct(&s, c)
}
Use omitempty in the field tags.
From the docs: https://golang.org/pkg/encoding/json/
Struct values encode as JSON objects. Each exported struct field
becomes a member of the object unless
the field's tag is "-", or
the field is empty and its tag specifies the "omitempty" option.
Field int json:"myName,omitempty"
I saw somewhere, but I do not remember where, that a slice struct is passing through function like the following code snippet.
package main
import "fmt"
func passSlice(arg interface{}) {
fmt.Println(arg)
}
func main() {
res := []struct {
Name string
}{}
passSlice(res)
}
I have no idea, how to use here a slice struct in the function. Have someone a idea, how can I use it in the function?
In order to use the slice struct (or any other value stored in an interface), you must first do a type assertion or type switch:
Type assertion:
func passSlice(arg interface{}) {
// Put args value in v if it is of type []struct{ Name string }
v, ok := arg.([]struct{ Name string })
if !ok {
// did not contain a value of type []struct{Name string}
return
}
for _, s := range v {
fmt.Println(s.Name)
}
}
Playground: http://play.golang.org/p/KiFeVC3VQ_
Type switches are similar, but can have cases for multiple types.
There is also an option of using the reflect package, allowing you to more dynamically handle interface values without knowing before hand what types you can expect, but using reflection is also more complex. To know more about using reflection in Golang, you can look here:
Laws of reflection
reflect package