Golang reflection - get actual value from StructField

Viewed 54

The code is:

type Root struct {
    One Nested
    Two Nested
}

type Nested struct {
    i int
    s string
}

I need to iterate over Root's fields and get the actual values of the primitives stored within the Nested objects. So far I have managed to iterate over Nested structs and get their name - with the following code:

rootType := reflect.Indirect(reflect.ValueOf(&rootObject)).Type()
    for i, limit := 0, rootType.NumField(); i < limit; i++ {
        nestedField := rootType.Field(i)

        name := nestedField.Name
}

I take it the only way to get access to actual objects is with the reflect.Indirect(reflect.Value) function. However, I am having trouble obtaining the actual Values of the Nested objects. All I have so far are StructFields.

1 Answers

Using FieldByName

As with all things in Go reflection, there are a few ways to go about doing things which will be more or less useful depending on your application. See: https://pkg.go.dev/reflect#Value.FieldByName. You could use a similar approach with Field.

package main

import (
    "log"
    "reflect"
)

type Root struct {
    One Nested
    Two Nested
}

type Nested struct {
    i int
    s string
}

func main() {
    r := Root{
        One: Nested{i: 1, s: "one"},
        Two: Nested{i: 2, s: "two"},
    }

    rv := reflect.Indirect(reflect.ValueOf(&r))
    rt := rv.Type()
    for i, limit := 0, rt.NumField(); i < limit; i++ {
        fld := rt.Field(i)

        v := rv.FieldByName(fld.Name).Addr().Interface()
        nv, ok := v.(*Nested)
        if !ok {
            log.Fatalf("field is type %T not Nested", v)
        }

        log.Printf("field name: %s, type: %T, i: %v, s: %v", fld.Name, v, nv.i, nv.s)
    }
}
2022/09/05 16:25:56 field name: One, type: *main.Nested, i: 1, s: one
2022/09/05 16:25:56 field name: Two, type: *main.Nested, i: 2, s: two

Using a map

I would think hard about your architectural choices. In my experience, there are relatively few problems that reflection can solve that can't also be solved by better design/data structure choices. For example:

package main

import (
    "log"
)

type Nested struct {
    i int
    s string
}

func main() {
    r := map[string]*Nested{
        "One": {i: 1, s: "one"},
        "Two": {i: 2, s: "two"},
    }

    for k, v := range r {
        log.Printf("key name: %s, type: %T, i: %v, s: %v", k, v, v.i, v.s)
    }
}
2022/09/05 16:31:46 key name: One, type: *main.Nested, i: 1, s: one
2022/09/05 16:31:46 key name: Two, type: *main.Nested, i: 2, s: two

Using a method

If you can live without the field name and won't be expanding the Root structure, you can define a simple helper method to let you iterate through the fields:

package main

import (
    "log"
)

type Root struct {
    One Nested
    Two Nested
}

func (r *Root) Array() []*Nested {
    return []*Nested{&r.One, &r.Two}
}

type Nested struct {
    i int
    s string
}

func main() {
    r := Root{
        One: Nested{i: 1, s: "one"},
        Two: Nested{i: 2, s: "two"},
    }

    for _, v := range r.Array() {
        log.Printf("type: %T, i: %v, s: %v", v, v.i, v.s)
    }
}
2022/09/05 16:38:01 type: *main.Nested, i: 1, s: one
2022/09/05 16:38:01 type: *main.Nested, i: 2, s: two
Related