Other ways of verifying reflect.Type for int and float64 - go

In golang, a number in JSON message is always parsed into float64.
In order to detect if it is actually integer, I am using reflect.TypeOf() to check its type.
Unfortunately there is no constant that represents reflect.Type.
intType := reflect.TypeOf(0)
floatType := reflect.TypeOf(0.0)
myType := reflect.TypeOf(myVar)
if myType == intType {
// do something
}
Is there more elegant solution instead of using 0 or 0.0 to get reflect.Type?

You may also use the Value.Kind() or Type.Kind() method whose possible values are listed as constants in the reflect package, at the doc of the Kind type.
myType := reflect.TypeOf(myVar)
if k := myType.Kind(); k == reflect.Int {
fmt.Println("It's of type int")
} else if k == reflect.Float64 {
fmt.Println("It's of type float64")
}
You can also use it in a switch:
switch myType.Kind() {
case reflect.Int:
fmt.Println("int")
case reflect.Float64:
fmt.Println("float64")
default:
fmt.Println("Some other type")
}
Note that both reflect.Type and reflect.Value has a Kind() method, so you can use it if you start with reflect.ValueOf(myVar) and also if you start with reflect.TypeOf(myVar).

To check if interface is of a specific type you can use type assertion with two return values, the second return value is a boolean indicating if the variable is of the type specified. And unlike with a single return value, it will not panic if the variable is of a wrong type.
if v, ok := myVar.(int); ok {
// type assertion succeeded and v is myVar asserted to type int
} else {
// type assertion failed, myVar wasn't an int
}
If there's more types that you need to check then using a type switch is a good idea:
switch v := myVar.(type) {
case int:
// v has type int
case float64:
// v has type float64
default:
// myVar was something other than int or float64
}
Note however that neither of these actually solve your problem, because like you say, numbers in JSON documents are always parsed into float64s. So if myVar is a parsed JSON number, it will always have type of float64 instead of int.
To solve this, I suggest you use the UseNumber() method of the json.Decoder, which causes the decoder to parse numbers as type Number, instead of float64. Take a look at https://golang.org/pkg/encoding/json/#Number
// Assume myVar is a value decoded with json.Decoder with UseNumber() called
if n, ok := myVar.(json.Number); ok {
// myVar was a number, let's see if its float64 or int64
// Check for int64 first because floats can be parsed as ints but not the other way around
if v, err := n.Int64(); err != nil {
// The number was an integer, v has type of int64
}
if v, err := n.Float64(); err != nil {
// The number was a float, v has type of float64
}
} else {
// myVar wasn't a number at all
}

Related

Clubbing values of type switch [duplicate]

This question already has an answer here:
golang multiple case in type switch
(1 answer)
Closed 10 months ago.
Following code is working fine
var requestMap map[string]interface{}
for _, value := range requestMap {
switch v := value.(type) {
case []interface{}:
if len(v) == 0 {
// if is empty then no need to throw NA
return http.StatusOK, nil
}
case string:
if len(v) == 0 {
// if is empty then no need to throw NA
return http.StatusOK, nil
}
}
}
But following code is giving invalid argument for len function, I have read this question
var requestMap map[string]interface{}
for _, value := range requestMap {
switch v := value.(type) {
case []interface{}, string:
if len(v) == 0 {
// if is empty then no need to throw NA
return http.StatusOK, nil
}
}
}
Isn't this case statement enough to identify []interface{} or string as value type ?
Why is it still considering interface{} as parameter of len()
If you list multiple types in a case of a type switch, the static type of the switch variable will be of the type of the original variable. Spec: Switch statements:
In clauses with a case listing exactly one type, the variable has that type; otherwise, the variable has the type of the expression in the TypeSwitchGuard.
So if the switch expression is v := value.(type), the of v in the matching case (listing multiple types) will be the type of value, in your case it will be interface{}. And the builtin len() function does not allow to pass values of type interface{}.
This is because in the case of the type switch, the v should be converted to a slice of interface ([]interface) and to a string at the same time. The compiler can't decide which to use, so it revert back the value to interface{}, since it can be anything.

Golang data as interface{} panics

Gophers,
I'm trying to implement the reflect package of Go and really stuck on one thing.
context - I'm trying to call an API that returns - time.Time and some data in interface{}. This data could be either int/int64 or float32/float64 for the most part. I take the data in interface{} and further create a struct where I keep the interface{} data in interface as reflect promises quite a few fancy things that I could do with the interface
type dataStore struct {
CreateTime time.Time
Data interface{}
}
I then create amap[string][]dataStore to store the data from the API that I fetch.
I'm trying to do the following to get the Float64 values that I know are coming and I want to do some math on them:
x := map[string][]dataStore {}
ReadDatafromAPI(x) // I call the API to read the data into variable x
//Assume map["CA"][]dataStore{{2020-03-31 21:55:52.123456, 123.4567890123e10},}
fmt.Println(x["CA"][0].Data) // This prints the data 123.4567890123e10, no problem
fmt.Println(reflect.ValueOf(x["CA"][0].Data))// this prints reflect.Value
va := reflect.ValueOf(x["CA"][0].Data)
fmt.Println(va.(float64)) // panic: interface conversion: interface {} is reflect.Value, not float64
fmt.Println(va.Interface()) // prints 123.4567890123e10
fmt.Println(va.Kind()) // prints struct
fmt.Println(va.NumField()) // prints 2 and I can fetch the fields using Field(0) and Field(1) -- the data doesn't make sense to me. not sure if those are pointers or what
I have just one objective - to fetch float64 as float64 and int as int. Meaning use reflect the way it is supposed to be used.
any insight would be highly appreciated.
Thanks in advance.
Folks - thanks for all the answers and pointers. I appreciate this! Looks like I still get reflect.Value as the type and not float64/32 as expected. See below:
switch flt := x["CA"][0].Data.(type) {
case float64:
fmt.Println("Data is float64 -> ", flt)
fmt.Printf("Type for flt is %T -> ", flt)
case float32:
fmt.Println("Data is float32 -> ", flt)
fmt.Printf("Type for flt is %T -> ", flt)
default:
fmt.Println("Its default!")
fmt.Printf("Type for flt is %T -> ", flt) // I always get here with reflect.Value as the Type. Never a float64 which is the value store without any question
}
if nflt, ok := x["CA"][0].Data.(float64); ok {
fmt.Println("nflt is float64")
} else {
fmt.Println("nflt is not float64) // I always get to Else
}
You don't need reflection to convert an interface to one of the known types. You need type assertions or a type switch:
if flt, ok:=data.(float64); ok {
// flt is float64
}
if i, ok:=data.(int); ok {
// i is int
}
Or:
switch k:=data.(type) {
case float64:
// Here, k is float64
case int:
// Here, k is int
}
Use a two-value type assertion to get the value without panicking:
d, ok := (x["CA"][0].Data.(float64)
if ok {
// it's a float64, do something with d.
}
or use a type switch:
switch d := x["CA"][0].Data.(type) {
case int:
// d is an int
case float64:
// d is a float64
... and other types as needed
default:
// handle unexpected type
}

Does type assertion change the value in go?

Go newbie here.
I have a map where the key arguments should be []string.
However, if I try to use the value directly arguments := m["arguments"] it doesn't seem to be the right type. When used later to append to another slice with arguments... I get Cannot use 'arguments' (type interface{}) as type []string.
I fixed this by chaning the assignment to a type check arguments, _ := m["arguments"].([]string). That works, but I'm not sure why. Is type assertion doing conversion as well?
The full example is below:
import (
"github.com/fatih/structs"
"strings"
)
var playbookKeyDict = map[string]string{
"Playbook": "",
"Limit" : "--limit",
"ExtraVars" : "--extra-vars",
}
type Playbook struct {
Playbook string `json:"playbook" xml:"playbook" form:"playbook" query:"playbook"`
Limit string `json:"limit" xml:"limit" form:"limit" query:"limit"`
ExtraVars string `json:"extra-vars" xml:"extra-vars" form:"extra-vars" query:"extra-vars"`
Arguments []string `json:"arguments" xml:"arguments" form:"arguments" query:"arguments"`
Args []string
}
func (p *Playbook) formatArgs() {
// is it worth iterating through directly with reflection instead of using structs import?
// https://stackoverflow.com/questions/21246642/iterate-over-string-fields-in-struct
m := structs.Map(p)
// direct assignment has the wrong type?
// arguments := m["arguments"]
arguments, _ := m["arguments"].([]string)
delete(m, "arguments")
for k, v := range m {
// Ignore non-strings and empty strings
if val, ok := v.(string); ok && val != "" {
key := playbookKeyDict[k]
if key == "" {
p.Args = append(p.Args, val)
} else {
p.Args = append(p.Args, playbookKeyDict[k], val)
}
}
}
p.Args = append(p.Args, arguments...)
}
Type assertion is used to get a value wrapped around using interface.
m := structs.Map(p)
Map(v interface{}){}
Map function is actually taking interface as its argument in the case stated. It is wrapping the type which is []string and its underlying value which is slice. The type can be checked using Relection reflect.TypeOf().
func TypeOf(i interface{}) Type
According to Russ Cox blog on Interfaces
Interface values are represented as a two-word pair giving a pointer
to information about the type stored in the interface and a pointer to
the associated data.
As specified in Golang spec
For an expression x of interface type and a type T, the primary
expression
x.(T)
asserts that x is not nil and that the value stored in x is of type T.
The notation x.(T) is called a type assertion.
For the error part:-
Cannot use 'arguments' (type interface{}) as type []string
We first needs to get the underlying value of type []string from interface using type assertion.

Get all fields from an interface

How do I know the fields I can access from the reply object/interface? I tried reflection but it seems you have to know the field name first. What if I need to know all the fields available to me?
// Do sends a command to the server and returns the received reply.
Do(commandName string, args ...interface{}) (reply interface{}, err error)
You can use the reflect.TypeOf() function to obtain a reflect.Type type descriptor. From there, you can list fields of the dynamic value stored in the interface.
Example:
type Point struct {
X int
Y int
}
var reply interface{} = Point{1, 2}
t := reflect.TypeOf(reply)
for i := 0; i < t.NumField(); i++ {
fmt.Printf("%+v\n", t.Field(i))
}
Output:
{Name:X PkgPath: Type:int Tag: Offset:0 Index:[0] Anonymous:false}
{Name:Y PkgPath: Type:int Tag: Offset:4 Index:[1] Anonymous:false}
The result of a Type.Field() call is a reflect.StructField value which is a struct, containing the name of the field among other things:
type StructField struct {
// Name is the field name.
Name string
// ...
}
If you also want the values of the fields, you may use reflect.ValueOf() to obtain a reflect.Value(), and then you may use Value.Field() or Value.FieldByName():
v := reflect.ValueOf(reply)
for i := 0; i < v.NumField(); i++ {
fmt.Println(v.Field(i))
}
Output:
1
2
Try it on the Go Playground.
Note: often a pointer to struct is wrapped in an interface. In such cases you may use Type.Elem() and Value.Elem() to "navigate" to the pointed type or value:
t := reflect.TypeOf(reply).Elem()
v := reflect.ValueOf(reply).Elem()
If you don't know whether it's a pointer or not, you can check it with Type.Kind() and Value.Kind(), comparing the result with reflect.Ptr:
t := reflect.TypeOf(reply)
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
// ...
v := reflect.ValueOf(reply)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
Try this variant on the Go Playground.
For a detailed introduction to Go's reflection, read the blog post: The Laws of Reflection.

Get type of an uninitialized slice

I want to return a type of an interface{}, while the input value might be var m []*MyModel
I've managed to get to the type *MyModel, while MyModel not as a pointer seems to be unreachable to me.
func getType( m interface{} ) reflect.Type {
t := reflect.TypeOf( m );
v := reflect.ValueOf( m );
if t.Kind() == reflect.Ptr {
if v.IsValid() && !v.IsNil() {
return getType( v.Elem().Interface() );
}
panic( "We have a problem" );
}
if t.Kind() == reflect.Slice {
if v.Len() == 0 {
s := reflect.MakeSlice( t , 1 , 1 );
return getType( s.Interface() );
}
return getType( v.Index( 0 ).Interface() );
}
return t;
}
Is it possible?
You may use Type.Elem() to get the type's element type, which works for Array, Chan, Map, Ptr and Slice.
You may run a loop and "navigate" to the type's element type until the type is not a pointer nor a slice (nor array, chan, map if you need so).
So the simple solution is this:
func getElemType(a interface{}) reflect.Type {
for t := reflect.TypeOf(a); ; {
switch t.Kind() {
case reflect.Ptr, reflect.Slice:
t = t.Elem()
default:
return t
}
}
}
Testing it:
type MyModel struct{}
fmt.Println(getElemType(MyModel{}))
fmt.Println(getElemType(&MyModel{}))
fmt.Println(getElemType([]MyModel{}))
fmt.Println(getElemType([]*MyModel{}))
fmt.Println(getElemType(&[]*MyModel{}))
fmt.Println(getElemType(&[]****MyModel{}))
fmt.Println(getElemType(&[][]**[]*[]***MyModel{}))
var p *[][]**[]*[]***MyModel
fmt.Println(p) // It's nil!
fmt.Println(getElemType(p))
Output (try it on the Go Playground):
main.MyModel
main.MyModel
main.MyModel
main.MyModel
main.MyModel
main.MyModel
main.MyModel
<nil>
main.MyModel
As you can see, no matter how "deep" we go with slices and pointers (&[][]**[]*[]***MyModel{}), getElemType() is able to extract main.MyModel.
One thing to note is that in my solution I used reflect.Type and not reflect.Value. Go is a statically typed language, so the type information is there even if pointers and slice elements are not "populated", even if we pass a "typed" nil such as p, we're still able to navigate through the "type chain".
Note: The above getElemType() panics if called with an untyped nil value, e.g. getElemType(nil), because in this case there is no type information available. To defend this, you may add a simple check:
if a == nil {
return nil
}
Note #2: Since the implementation contains a loop without limiting iteration count, values of recursive types will drive it into an endless loop, such as:
type RecType []RecType
getElemType(RecType{}) // Endless loop!

Resources