Golang How to get value from any/interface

eye-catch Golang

You definitely see some function that has a parameter with interface{} data type. How can we read a property’s value from it?

Sponsored links

Check if the variable is built-in data type

The data type of the parameter can be checked with param.(type).

func acceptAnyValue(arg any) {
    switch v := arg.(type) {
    case string:
        fmt.Printf("String: %s", v)
    case int:
        fmt.Printf("Int32: %d", v)
    case float64:
        fmt.Printf("float64: %f", v)
    case map[string]int:
	fmt.Printf("map[string]int: %+v", v)
    case map[int]string:
	fmt.Printf("map[int]string: %+v", v)
    case map[string]map[any]any:
	fmt.Printf("map[string]map[any]any: %+v", v)
    case []int:
	fmt.Printf("[]int: %+v", v)
    default:
        fmt.Printf("Undefined type: %s", reflect.TypeOf(v))
    }

    fmt.Println()
}

We can write each handling depending on the data type.

acceptAnyValue(523)     // Int32: 523
acceptAnyValue(11.123)  // float64: 11.123000
acceptAnyValue("value") // String: value
map1 := make(map[string]int)
map1["prop1"] = 1
map1["prop2"] = 2
acceptAnyValue(map1)  // map[string]int: map[prop1:1 prop2:2]
map2 := make(map[int]string)
map2[1] = "ABC"
map2[2] = "XYZ"
acceptAnyValue(map2)  // map[int]string: map[1:ABC 2:XYZ]
map3 := make(map[string]map[int]string)
map3["first"] = map2
acceptAnyValue(map3)  // Undefined type: map[string]map[int]string
acceptAnyValue(map3)
acceptAnyValue([]int{1, 2, 3})   // []int: [1 2 3]
acceptAnyValue([]int64{1, 2, 3}) // Undefined type: []int64

If the data type is different for map, the value is handled in the default statement. Likewise, array of int64 is not defined in the switch-case, it is handled in the default statement.

Sponsored links

Check if the variable is predefined struct type

Even if the parameter is a struct, we can write it in the same way.

type unknownObj1 struct {
    Name  string
    Prop1 string
}

type unknownObj2 struct {
    Obj   unknownObj1
    Count int
    Name string
    Prop1 string
}

func acceptAnyValue(arg any) {
    switch v := arg.(type) {
    case unknownObj1:
        fmt.Printf("Name: %s, Prop1: %s", v.Name, v.Prop1)
    case unknownObj2:
        fmt.Println(reflect.TypeOf(arg) == reflect.TypeOf(unknownObj1{}) )    // true
        fmt.Println(reflect.TypeOf(arg) == reflect.TypeOf(unknownObj2{}) )  // false
        fmt.Printf("Name: %s, Prop1: %s, Count: %d", v.Obj.Name, v.Obj.Prop1, v.Count)
    default:
        fmt.Println(reflect.TypeOf(arg) == reflect.TypeOf(unknownObj1{}) )    // false
        fmt.Println(reflect.TypeOf(arg) == reflect.TypeOf(unknownObj2{}) )  // true
        fmt.Printf("Undefined type: %s", reflect.TypeOf(v))
    }

    fmt.Println()
}

Just use struct type in the switch case. Then, IntelliSense knows the data type and can support our coding.

If we only want to know the data type, we can write it in the following way.

reflect.TypeOf(arg) == reflect.TypeOf(unknownObj1{})
reflect.TypeOf(arg) == reflect.TypeOf(unknownObj2{})

The output result is the following.

obj1 := unknownObj1{
    Name: "Yuto", Prop1: "Hello World!",
}
obj2 := unknownObj2{
    Obj: unknownObj1{
        Name: "Agent", Prop1: "ABCDE",
    },
    Count: 66,
    Name: "Yuto-2",
    Prop1: "Level 1",
}
acceptAnyValue(obj1)
// true
// false
// Name: Yuto, Prop1: Hello World!
acceptAnyValue(obj2)
// false
// true
// Name: Agent, Prop1: ABCDE, Count: 66

How to get value by property name without knowing the type

There might be some cases where a value needs to be read from a fixed property name without knowing the data type.

For example, if a function receives different struct types but all of them have the same property name, we don’t want to write lots of switch-case. Instead, just want to have one line to read the value in the following way.

func acceptUnknownType(arg any) {
    fmt.Println(getValueOf(arg, "Name"))
    fmt.Println(getValueOf(arg, "Prop1"))
    fmt.Println(getValueOf(arg, "UnknownProp"))
}

To achieve this, we need to use reflect package. If the property exists, it returns a value, otherwise nil.

import "reflect"
func getValueOf(obj any, field string) any {
    value := reflect.ValueOf(obj)
    val := reflect.Indirect(value).FieldByName(field)
    if val.IsValid() {
        return val
    }
    return nil
}

It’s also ok to return both depending on your needs.

return val, val.IsValid()

Then, let’s try to use it.

obj1 := unknownObj1{
    Name: "Yuto", Prop1: "Hello World!",
}
obj2 := unknownObj2{
    Obj: unknownObj1{
        Name: "Agent", Prop1: "ABCDE",
    },
    Count: 66,
    Name:  "Yuto-2",
    Prop1: "Level 1",
}
acceptUnknownType(obj1)
// Yuto
// Hello World!
// <nil>
acceptUnknownType(obj2)
// Yuto-2
// Level 1
// <nil>

Related Articles

Comments

Copied title and URL