golangの日記

Go言語を中心にプログラミングについてのブログ

Go言語(golang) 構造体のフィールド名、値、型を再帰的に列挙する

golang.png



reflectパッケージを使ってループでフィールド名、値、型を出力する

package main

import (
    "fmt"
    "reflect"
)

func main() {
    s := struct {
        String  string
        Integer int
        Slice   []int
    }{
        String:  "hello",
        Integer: 100,
        Slice:   []int{10, 20, 30},
    }

    rv := reflect.ValueOf(s)
    rt := rv.Type()
    // NumFieldでフィールド数を取得
    for i := 0; i < rt.NumField(); i++ {
        field := rt.Field(i)
        kind := field.Type.Kind() // 型
        value := rv.FieldByName(field.Name) // value は interface です。
        fmt.Printf("name: %v, value %v, kind: %v\n", field.Name, value, kind)
    }
    // 実行結果
    // name: String, value hello, kind: string
    // name: Integer, value 100, kind: int
    // name: Slice, value [10 20 30], kind: slice
}



再帰的にフィールド名、値、型を出力する

package main

import (
    "fmt"
    "reflect"
    "strings"
)

// reflect.Value を返す関数
func toReflectValue(i interface{}) (reflect.Value, bool) {
    rv, ok := i.(reflect.Value)
    if !ok {
        rv = reflect.ValueOf(i)
    }

    switch rv.Kind() {
    case reflect.Ptr:
        // ポインタ以外になるまで reflect.Indirect する
        return toReflectValue(reflect.Indirect(rv))
    case reflect.Struct:
        return rv, true
    }
    // 構造体以外
    return rv, false
}

func output(depth int, name string, value interface{}, kind reflect.Kind) {
    indent := strings.Repeat("  ", depth)
    if kind == reflect.Struct {
        fmt.Printf("%s- FieldName: %s, Type: %v\n", indent, name, kind)
    } else {
        fmt.Printf("%s- FieldName: %s, Value: %v, Type: %v\n", indent, name, value, kind)
    }
}

func recursive(depth int, rv reflect.Value) {
    typ := rv.Type()
    for i := 0; i < typ.NumField(); i++ {
        field := typ.Field(i)
        kind := field.Type.Kind()
        value := rv.FieldByName(field.Name)

        if kind == reflect.Ptr {
            vv, isStructure := toReflectValue(value)
            output(depth, field.Name, vv.Interface(), vv.Kind())
            if isStructure {
                recursive(depth+1, vv)
            }
        } else {
            output(depth, field.Name, value, kind)
            if kind == reflect.Struct {
                recursive(depth+1, value)
            }
        }
    }
}

func main() {
    type User struct {
        Name string
        Age  int
    }

    type Users struct {
        User1 *User
        User2 *User
    }

    s := struct {
        User    *User
        Users   *Users
    }{
        User: &User{
            Name: "Tanaka",
            Age:  31,
        },
        Users: &Users{
            &User{"Matsui", 45},
            &User{"Suzuki", 46},
        },
    }

    rv, _ := toReflectValue(s)
    recursive(0, rv)
    // 実行結果
    // - FieldName: User, Type: struct
    //   - FieldName: Name, Value: Tanaka, Type: string
    //   - FieldName: Age, Value: 31, Type: int
    // - FieldName: Users, Type: struct
    //   - FieldName: User1, Type: struct
    //     - FieldName: Name, Value: Matsui, Type: string
    //     - FieldName: Age, Value: 45, Type: int
    //   - FieldName: User2, Type: struct
    //     - FieldName: Name, Value: Suzuki, Type: string
    //     - FieldName: Age, Value: 46, Type: int

}