Golang为深层嵌套结构赋值

时间:2016-12-27 02:36:07

标签: javascript go dynamic struct slice

我学习Go,到目前为止真的很享受。来自JS背景,我仍然会发现某些模式和最佳实践。

使用Object路径获取并为Go中的深层嵌套对象赋值的最佳方法是什么?例如,在JS中可以这样做......



var children = [{children:[{children:[{a:1}]}]}]
var child = "0.children.0.children.0".split('.').reduce((c, p) => c[p], children)
child.a = 2
console.log(children[0].children[0].children[0].a)




1 个答案:

答案 0 :(得分:3)

如果您需要一般解决方案,可以使用包reflect来实现,但如果可能的话,最好避免使用它(例如,如果您知道类型和"路径&#34 ;在编译时,只需使用字段selectorsindex expressions)。

这是一个示范。帮助函数设置"深" string元素指定的值可能如下所示:

func set(d interface{}, value interface{}, path ...string) {
    v := reflect.ValueOf(d)
    for _, s := range path {
        v = index(v, s)
    }
    v.Set(reflect.ValueOf(value))
}

上面使用的index()函数可能如下所示:

func index(v reflect.Value, idx string) reflect.Value {
    if i, err := strconv.Atoi(idx); err == nil {
        return v.Index(i)
    }
    return v.FieldByName(idx)
}

我们可以测试它:

type Foo struct {
    Children []Foo
    A        int
}

func main() {
    x := []Foo{
        {
            Children: []Foo{
                {
                    Children: []Foo{
                        {
                            A: 1,
                        },
                    },
                },
            },
        },
    }
    fmt.Printf("%+v\n", x)
    path := "0.Children.0.Children.0.A"
    set(x, 2, strings.Split(path, ".")...)
    fmt.Printf("%+v\n", x)
}

输出(在Go Playground上尝试):

[{Children:[{Children:[{Children:[] A:1}] A:0}] A:0}]
[{Children:[{Children:[{Children:[] A:2}] A:0}] A:0}]

从输出中可以看出,"深" A路径string表示的字段"0.Children.0.Children.0.A"已从初始1更改为2

请注意,必须导出结构的字段(在这种情况下为Foo.AFoo.Children)(必须以大写字母开头),否则其他包将无法访问这些字段及其值无法使用包reflect更改。

没有反思,知道类型和"路径"之前,它可以这样做(继续上一个例子):

f := &x[0].Children[0].Children[0]
fmt.Printf("%+v\n", f)
f.A = 3
fmt.Printf("%+v\n", f)

输出(在Go Playground上尝试):

&{Children:[] A:2}
&{Children:[] A:3}

这个的一般解决方案(没有反思):

func getFoo(x []Foo, path ...string) (f *Foo) {
    for _, s := range path {
        if i, err := strconv.Atoi(s); err != nil {
            panic(err)
        } else {
            f = &x[i]
            x = f.Children
        }
    }
    return
}

使用它(再次,继续上一个例子):

path = "0.0.0"
f2 := getFoo(x, strings.Split(path, ".")...)
fmt.Printf("%+v\n", f2)
f2.A = 4
fmt.Printf("%+v\n", f2)

输出(在Go Playground上尝试):

&{Children:[] A:3}
&{Children:[] A:4}

但请注意,如果我们只处理int索引,那么将path声明为...string[]string就没有任何意义了),int切片会更有意义。