I have the following code snippet that after going some reflection, it sets a struct's field to a string value
switch fType := v.(type) {
case MyCompositeFlagString:
s, ok := userInput.(string)
if !ok {
log.Printf("Erroneous input type:%T and input value: %v\n", userInput, userInput)
return ErrUnexpectedInput
}
valueField := values.Elem().Field(i).FieldByName("MyFlagString").FieldByName("Value")
valueField.SetString(s)
I don't see any SetSlice method in reflect package.
How can I perform the above operation when the valueField is of type []string ?
Value.SetString() is a convenience method for setting string values. There isn't a separate method for all types for obvious reasons, but there is a "generic" Value.Set() method, you may use that. You just have to obtain a reflect.Value from the value you want to set:
var someSlice ...
valueField.Set(reflect.ValueOf(someSlice))
Related
I have a variable that needs to be either a string or map[string]string (will be deserializing from JSON). So I declare it as interface{}. How can I check that the value is map[string]string?
This question How to check interface is a map[string]string in golang almost answers my question. But the accepted answer only works if the variable is declared as a map[string]string not if the variable is interface{}.
package main
import (
"fmt"
)
func main() {
var myMap interface{}
myMap = map[string]interface{}{
"foo": "bar",
}
_, ok := myMap.(map[string]string)
if !ok {
fmt.Println("This will be printed")
}
}
See https://play.golang.org/p/mA-CVk7bdb9
I can use two type assertions though. One on the map and one on the map value.
package main
import (
"fmt"
)
func main() {
var myMap interface{}
myMap = map[string]interface{}{
"foo": "bar",
}
valueMap, ok := myMap.(map[string]interface{})
if !ok {
fmt.Println("will not be printed")
}
for _, v := range valueMap {
if _, ok := v.(string); !ok {
fmt.Println("will not be printed")
}
}
}
See https://play.golang.org/p/hCl8eBcKSqE
Question: is there a better way?
If you declare a variable as type interface{}, it is type interface{}. It is not, ever, some map[keytype]valuetype value. But a variable of type interface{} can hold a value that has some other concrete type. When it does so, it does so—that's all there is to it. It still is type interface{}, but it holds a value of some other type.
An interface value has two parts
The key distinction here is between what an interface{} variable is, and what it holds. Any interface variable actually has two slots inside it: one to hold what type is stored in it, and one to hold what value is stored in it. Any time you—or anyone—assign a value to the variable, the compiler fills in both slots: the type, from the type of the value you used, and the value, from the value you used.1 The interface variable compares equal to nil if it has nil in both slots; and that's also the default zero value.
Hence, your runtime test:
valueMap, ok := myMap.(map[string]interface{})
is a sensible thing to do: if myMap holds a value that has type map[string]interface, ok gets set to true and valueMap contains the value (which has that type). If myMap holds a value with some other type, ok gets set to false and valueMap gets set to the zero-value of type map[string]interface{}. In other words, at runtime, the code checks the type-slot first, then either copies the value-slot across to valueMap and sets ok to true, or sets valueMap to nil and sets ok to false.
If and when ok has been set to true, each valueMap[k] value is type interface{}. As before, for myMap itself, each of these interface{} variables can—but do not have to—hold a value of type string, and you must use some sort of "what is the actual type-and-value" run-time test to tease them apart.
When you use json.Unmarshal to stuff decoded JSON into a variable of type interface{}, it is capable of deserializing any of these documented JSON types. The list then tells you what type gets stuffed into the interface variable:
bool, for JSON booleans
float64, for JSON numbers
string, for JSON strings
[]interface{}, for JSON arrays
map[string]interface{}, for JSON objects
nil for JSON null
So after doing json.Unmarshal into a variable of type interface{}, you should check what type got put into the type-slot of the variable. You can do this with an assertion and an ok boolean, or you can, if you prefer, use a type switch to decode it:
var i interface
if err := json.Unmarshal(data, &i); err != nil {
panic(err)
}
switch v := i.(type) {
case string:
... code ...
case map[string]interface{}:
... code ...
... add some or all of the types listed ...
}
The thing is, no matter what you do in code here, you did have json.Unmarshal put something into an interface{}, and interface{} is the type of i. You must test at runtime what type and value pair the interface holds.
Your other option is to inspect your JSON strings manually and decide what type of variable to provide to json.Unmarshal. That gives you less code to write after the Unmarshal, but more code to write before it.
There's a more complete example here, on the Go playground, of using type switches to inspect the result from a json.Unmarshal. It's deliberately incomplete but, I think, has enough input and output cases to let you work out how to handle everything, given the quote above about what json.Unmarshal writes into a variable of type interface{}.
1Of course, if you assign one interface{} from some other interface{}:
var i1, i2 interface{}
... set i1 from some actual value ...
// more code, then:
i2 = i1
the compiler just copies both slots from i1 into i2. The two-separate-slots thing becomes clearer when you do:
var f float64
... code that sets f to, say, 1.5 ...
i2 = f
for instance, as that writes float64 into the type-slot, and the value 1.5 into the value-slot. The compiler knows that f is float64 so the type-setting just means "stick a constant in it". The compiler doesn't necessarily know the value of f so the value-setting is a copy of whatever the actual value is.
I want to write a mockData method which can accept several types of parameter and return correspond objects based on its json data. The code as below:
func MockData(jsonPath string,v interface{})(interface{},error){
var ret interface{}
data,_ := ioutil.ReadFile(jsonPath)
switch v.(type) {
case Req:
ret = Req{}
fmt.Printf("\n===before Unmarshal==%T===\n",ret)
err = json.Unmarshal(data,&ret)
if err!=nil{...}
fmt.Printf("======after unmarshal===%T\n",ret)
case ...
default:
fmt.Printf("error===not match")
}
return ret,err
}
However, it panics when I use it. The code as below:
func main(){
reqJsonPath := /xx/yy/req.json
obj,err:=test.MockData(jsonFile,Req{})
if err!=nil{...}
require := obj.(Req) //panic cant []interface{} to Req
}
and the output of MockData is:
===before Unmarshal==Req===
======after unmarshal===[]interface{}
the type of object changed after unmarshal. and some more strange is that if I replace:
ret = Req{}
with
ret = &Req{}
the output will be same as below:
===before Unmarshal==*Req===
======after unmarshal===*Req
To reproduce the problem more conveniently I give the Require struct as below:
type Req []*Ele
type Ele struct {
ID int
Level int
}
summary:
Can I achieve expected function which produces different types of objects based on its json and type?
Why does the type of object changed after unmarshal, and why it not changed after I add &?
Can I achieve expected function which produces different types of objects based on its json and type?
func MockData(filename string, v interface{}) (interface{}, error) {
data, _ := ioutil.ReadFile(filename)
switch t := v.(type) {
case Req:
// t at this point is a Req{}
err := json.Unmarshal(data, &t)
return t, err
}
return nil, errors.New("unknown type")
}
I don't really know your motivation why you you need to pass an actual struct rather than a pointer. Check this demonstration
Why does the type of object changed after unmarshal, and why it not changed after I add &?
When you unmarshal using &ret where ret is an interface, you are getting the address of the interface. Hence, json.Unmarshal() will see that the backing data is a interface rather than a pointer to a struct. The default data type that json.Unmarshal() will use is map[string]interface{} for objects and []interface{} for arrays.
Now if you unmarshal using ret where ret is &Req{}, json.Unmarshal() will check that the backing data is a struct, hence it can do it's unmarshaling using the struct's fields.
Edit:
You seem to be confused by pointer to an interface which is different to an interface which has a pointer. Try this code and you'll see the difference.
var x interface{} = Req{}
var y interface{} = &x
var z interface{} = &Req{}
fmt.Printf("%T\n", y)
fmt.Printf("%T\n", z)
Remember that interfaces are just normal values and they also take memory. Now if you take an address of that memory, you get the pointer to the interface rather than the pointer to the data the interface is referring to.
Can I achieve expected function which produces different types of objects based on its json and type?
Yes, but you'll have to convert it back using a type assertion at the calling end ie
MyFoo:=MockData("foo.json", Foo{}).(Foo)
(or have multiple return ret.(Foo) return ret.(Bar) in the func)
Why does the type of object changed after unmarshal, and why it not changed after I add &?
There are some helpful comments in the top of the Unmarshal source
namely
// To unmarshal JSON into a pointer, Unmarshal first handles the case of
// the JSON being the JSON literal null. In that case, Unmarshal sets
// the pointer to nil. Otherwise, Unmarshal unmarshals the JSON into
// the value pointed at by the pointer. If the pointer is nil, Unmarshal
// allocates a new value for it to point to.
and
// To unmarshal JSON into an interface value,
// Unmarshal stores one of these in the interface value:
//
// bool, for JSON booleans
// float64, for JSON numbers
// string, for JSON strings
// []interface{}, for JSON arrays
// map[string]interface{}, for JSON objects
// nil for JSON null
So in the first case you are unmarshalling into an interface value (ret is declared as an interface{})
In the second case there is a pointer to a struct so that's what you get
fmt.Println(v.Kind())
fmt.Println(reflect.TypeOf(v))
How can I find out the type of the reflect value of a slice?
The above results in
v.Kind = slice
typeof = reflect.Value
When i try to Set it will crash if i create the wrong slice
t := reflect.TypeOf([]int{})
s := reflect.MakeSlice(t, 0, 0)
v.Set(s)
For example []int{} instead of []string{} so I need to know the exact slice type of the reflect value before I create one.
To start, we need to ensure that the we're dealing with a slice by testing: reflect.TypeOf(<var>).Kind() == reflect.Slice
Without that check, you risk a runtime panic. So, now that we know we're working with a slice, finding the element type is as simple as: typ := reflect.TypeOf(<var>).Elem()
Since we're likely expecting many different element types, we can use a switch statement to differentiate:
t := reflect.TypeOf(<var>)
if t.Kind() != reflect.Slice {
// handle non-slice vars
}
switch t.Elem().Kind() { // type of the slice element
case reflect.Int:
// Handle int case
case reflect.String:
// Handle string case
...
default:
// custom types or structs must be explicitly typed
// using calls to reflect.TypeOf on the defined type.
}
I'm not very clear about what this code snippet behaves.
func show(i interface{}) {
switch t := i.(type) {
case *Person:
t := reflect.TypeOf(i) //what t contains?
v := reflect.ValueOf(i) //what v contains?
tag := t.Elem().Field(0).Tag
name := v.Elem().Field(0).String()
}
}
What is the difference between the type and value in reflection?
reflect.TypeOf() returns a reflect.Type and reflect.ValueOf() returns a reflect.Value. A reflect.Type allows you to query information that is tied to all variables with the same type while reflect.Value allows you to query information and preform operations on data of an arbitrary type.
Also reflect.ValueOf(i).Type() is equivalent to reflect.TypeOf(i).
In the example above, you are using the reflect.Type to get the "tag" of the first field in the Person struct. You start out with the Type for *Person. To get the type information of Person, you used t.Elem(). Then you pulled the tag information about the first field using .Field(0).Tag. The actual value you passed, i, does not matter because the Tag of the first field is part of the type.
You used reflect.Value to get a string representation of the first field of the value i. First you used v.Elem() to get a Value for the struct pointed to by i, then accessed the first Field's data (.Field(0)), and finally turned that data into a string (.String()).
I need Go to implicitly resolve my struct type, in order to do generic replacement of some attribute.
//must replace the attribute with attValue
func SetAttribute(object interface{}, attributeName string, attValue interface{}, objectType reflect.Type) interface{} {
/// works perfectly, but function SetAttribute needs to know Customer type to do the convertion
convertedObject := object.(Customer) // <-- Need to hard code a cast :(
// doesn't works... raise panic!
//convertedObject := object
value := reflect.ValueOf(&convertedObject).Elem()
field := value.FieldByName(attributeName)
valueForAtt := reflect.ValueOf(attValue)
field.Set(valueForAtt)
return value.Interface()
}
Please check out full example in the Go playground...
http://play.golang.org/p/jxxSB5FKEy
convertedObject is the value of what is in the object interface. Taking the address of that has no effect on the original customer. (and converted is probably a poor prefix for the name, since that is generated from a "type assertion", not a "type conversion")
If you use object directly, it panics, because you're then taking the address of the interface, not the customer.
You need to pass the address of the customer you want to modify to the function:
SetAttribute(&customer, "Local", addressNew, reflect.TypeOf(Customer{}))
You can also have your SetAttribute check if it's a pointer first:
if reflect.ValueOf(object).Kind() != reflect.Ptr {
panic("need a pointer")
}
value := reflect.ValueOf(object).Elem()
field := value.FieldByName(attributeName)
valueForAtt := reflect.ValueOf(attValue)
field.Set(valueForAtt)
return value.Interface()