Go 如何将嵌套结构中的字段设置为零值?

Go 如何将嵌套结构中的字段设置为零值?,go,Go,假设我有一个structThing1的实例,我想json.Marshal type Thing1 struct { A string `json:"a,omitempty"` B int `json:"b,omitempty"` C Thing2 `json:"c,omitempty"` } type Thing2 struct { D bool `json:"d,omitempty"` E int `json:"e,omitempty"` }

假设我有一个struct
Thing1
的实例,我想
json.Marshal

type Thing1 struct {
    A string `json:"a,omitempty"`
    B int    `json:"b,omitempty"`
    C Thing2 `json:"c,omitempty"`
}

type Thing2 struct {
    D bool `json:"d,omitempty"`
    E int  `json:"e,omitempty"`
}

...

thing1 := Thing1{
    A: "test",
    B: 42,
    C: Thing2{D: true, E: 43},
}
您将如何编写一个函数,该函数接受任何结构的实例和要编辑的字段列表,并返回传入对象的克隆(或只是变异),但编辑的字段设置为零值

redact(thing1, []string{"B", "D"})
thing1 == Thing1{
    A: "test",
    B: 0,
    C: Thing2{D: false, E: 43},
}
我不能使用
json:“-”
作为字段标记,因为我使用的查询语言(Dgraph)需要当前的字段标记


编辑:不在本例中,但数组中的对象也应进行编辑(如果适用)

使用反射操作结构字段的值。下面是我在评论中所写内容的概念证明。由于这只是一个poc,您可能需要调整/修改代码以满足您的需要

此函数用于修改原始数据。代码是自解释的

func redact(target interface{}, fieldsToModify []string) {
    // if target is not pointer, then immediately return
    // modifying struct's field requires addresable object
    addrValue := reflect.ValueOf(target)
    if addrValue.Kind() != reflect.Ptr {
        return
    }

    // if target is not struct then immediatelly return
    // this might need to be modified as per your needs
    targetValue := addrValue.Elem()
    targetType := targetValue.Type()
    if targetType.Kind() != reflect.Struct {
        return
    }

    // loop the fields
    for i := 0; i < targetType.NumField(); i++ {
        fType := targetType.Field(i)
        fValue := targetValue.Field(i)

        // if the field type is struct, then call redact() recursively
        if fValue.Kind() == reflect.Struct {
            redact(fValue.Addr().Interface(), fieldsToModify)
            continue
        } 

        // if the field is slice, loop then call redact() recursively
        if fValue.Kind() == reflect.Array || fValue.Kind() == reflect.Slice {
            for i := 0; i < fValue.Len(); i++ {
                redact(fValue.Index(i).Addr().Interface(), fieldsToModify)
            }
            continue
        }

        // loop the fieldsToModify
        for _, fieldToModify := range fieldsToModify {
            if fieldToModify == fType.Name && fValue.CanSet() {
                fValue.Set(reflect.Zero(fType.Type))
            }
        }
    }
}

操场:

使用反射操作结构字段的值。下面是我在评论中所写内容的概念证明。由于这只是一个poc,您可能需要调整/修改代码以满足您的需要

此函数用于修改原始数据。代码是自解释的

func redact(target interface{}, fieldsToModify []string) {
    // if target is not pointer, then immediately return
    // modifying struct's field requires addresable object
    addrValue := reflect.ValueOf(target)
    if addrValue.Kind() != reflect.Ptr {
        return
    }

    // if target is not struct then immediatelly return
    // this might need to be modified as per your needs
    targetValue := addrValue.Elem()
    targetType := targetValue.Type()
    if targetType.Kind() != reflect.Struct {
        return
    }

    // loop the fields
    for i := 0; i < targetType.NumField(); i++ {
        fType := targetType.Field(i)
        fValue := targetValue.Field(i)

        // if the field type is struct, then call redact() recursively
        if fValue.Kind() == reflect.Struct {
            redact(fValue.Addr().Interface(), fieldsToModify)
            continue
        } 

        // if the field is slice, loop then call redact() recursively
        if fValue.Kind() == reflect.Array || fValue.Kind() == reflect.Slice {
            for i := 0; i < fValue.Len(); i++ {
                redact(fValue.Index(i).Addr().Interface(), fieldsToModify)
            }
            continue
        }

        // loop the fieldsToModify
        for _, fieldToModify := range fieldsToModify {
            if fieldToModify == fType.Name && fValue.CanSet() {
                fValue.Set(reflect.Zero(fType.Type))
            }
        }
    }
}

操场:

以下是如何使用reflect软件包:

func redact(x interface{}, names []string) error {
    // Starting value must be a pointer.
    v := reflect.ValueOf(x)
    if v.Kind() != reflect.Ptr {
        return errors.New("not pointer")
    }

    // Create map for easy lookup.
    m := make(map[string]bool)
    for _, name := range names {
        m[name] = true
    }

    redactValue(v, m)
    return nil
}

func redactValue(v reflect.Value, names map[string]bool) {
    switch v.Kind() {
    case reflect.Ptr:
        if v.IsZero() {
            return
        }
        redactValue(v.Elem(), names)
    case reflect.Interface:
        if v.IsZero() {
            return
        }
        iv := v.Elem()
        switch iv.Kind() {
        case reflect.Slice, reflect.Ptr:
            redactValue(iv, names)
        case reflect.Struct, reflect.Array:
            // Copy required for modification. 
            copy := reflect.New(iv.Type()).Elem()
            copy.Set(iv)
            redactValue(copy, names)
            v.Set(copy)
        }
    case reflect.Struct:
        t := v.Type()
        for i := 0; i < t.NumField(); i++ {
            sf := t.Field(i)
            ft := sf.Type
            fv := v.Field(i)
            if names[sf.Name] {
                // Clobber the field.
                fv.Set(reflect.Zero(ft))
                continue
            }
            redactValue(fv, names)
        }
    case reflect.Slice, reflect.Array:
        for i := 0; i < v.Len(); i++ {
            redactValue(v.Index(i), names)
        }

    }
}
func编校(x接口{},名称[]字符串)错误{
//起始值必须是指针。
v:=反射值(x)
如果v.Kind()!=reflect.Ptr{
返回错误。新建(“非指针”)
}
//创建地图以便于查找。
m:=make(映射[字符串]布尔)
对于u,名称:=范围名称{
m[name]=true
}
还原值(v,m)
归零
}
func redactValue(v reflect.Value,名称映射[string]bool){
开关v.种类(){
案例反映。Ptr:
如果v.IsZero(){
返回
}
redactValue(v.Elem(),名称)
案例。界面:
如果v.IsZero(){
返回
}
iv:=v.Elem()
开关四.种类(){
case reflect.Slice,reflect.Ptr:
修订价值(四、名称)
大小写reflect.Struct,reflect.Array:
//修改所需的副本。
复制:=reflect.New(iv.Type()).Elem()
复印件(四)
redactValue(副本、名称)
v、 套装(复印件)
}
case reflect.Struct:
t:=v.Type()
对于i:=0;i


这个答案处理结构、切片、数组、指针和接口。

下面介绍如何使用reflect包:

func redact(x interface{}, names []string) error {
    // Starting value must be a pointer.
    v := reflect.ValueOf(x)
    if v.Kind() != reflect.Ptr {
        return errors.New("not pointer")
    }

    // Create map for easy lookup.
    m := make(map[string]bool)
    for _, name := range names {
        m[name] = true
    }

    redactValue(v, m)
    return nil
}

func redactValue(v reflect.Value, names map[string]bool) {
    switch v.Kind() {
    case reflect.Ptr:
        if v.IsZero() {
            return
        }
        redactValue(v.Elem(), names)
    case reflect.Interface:
        if v.IsZero() {
            return
        }
        iv := v.Elem()
        switch iv.Kind() {
        case reflect.Slice, reflect.Ptr:
            redactValue(iv, names)
        case reflect.Struct, reflect.Array:
            // Copy required for modification. 
            copy := reflect.New(iv.Type()).Elem()
            copy.Set(iv)
            redactValue(copy, names)
            v.Set(copy)
        }
    case reflect.Struct:
        t := v.Type()
        for i := 0; i < t.NumField(); i++ {
            sf := t.Field(i)
            ft := sf.Type
            fv := v.Field(i)
            if names[sf.Name] {
                // Clobber the field.
                fv.Set(reflect.Zero(ft))
                continue
            }
            redactValue(fv, names)
        }
    case reflect.Slice, reflect.Array:
        for i := 0; i < v.Len(); i++ {
            redactValue(v.Index(i), names)
        }

    }
}
func编校(x接口{},名称[]字符串)错误{
//起始值必须是指针。
v:=反射值(x)
如果v.Kind()!=reflect.Ptr{
返回错误。新建(“非指针”)
}
//创建地图以便于查找。
m:=make(映射[字符串]布尔)
对于u,名称:=范围名称{
m[name]=true
}
还原值(v,m)
归零
}
func redactValue(v reflect.Value,名称映射[string]bool){
开关v.种类(){
案例反映。Ptr:
如果v.IsZero(){
返回
}
redactValue(v.Elem(),名称)
案例。界面:
如果v.IsZero(){
返回
}
iv:=v.Elem()
开关四.种类(){
case reflect.Slice,reflect.Ptr:
修订价值(四、名称)
大小写reflect.Struct,reflect.Array:
//修改所需的副本。
复制:=reflect.New(iv.Type()).Elem()
复印件(四)
redactValue(副本、名称)
v、 套装(复印件)
}
case reflect.Struct:
t:=v.Type()
对于i:=0;i


这个答案处理结构、切片、数组、指针和接口。

使用反射。尝试循环所有字段。将匹配字段的值设置为零值。如果字段类型为map/struct,则递归调用相同的编校函数。您这样做是为了从JSON输出中删除一些字段吗?您看过json修补程序库吗?这样可以让您在将字段封送到json后对其进行编校?请使用reflect。尝试循环所有字段。将匹配字段的值设置为零值。如果字段类型为map/struct,则递归调用相同的编校函数。您这样做是为了从JSON输出中删除一些字段吗?您看过json修补程序库吗,这样可以让您在封送到json后对字段进行编辑?忘记将其放在示例中了,但是如果Applicatable忘记将其放在示例中,那么数组中的对象也应该进行编辑,但是如果Applicatable忘了将其放在示例中,那么数组中的对象也应该进行编辑,但是,如果applicable@Iamok刚刚更新了我的答案。顺便说一句,就像我说的,这只是一个poc,您可能需要修改它以符合您的要求needs@Iamok不想冒犯,但你也需要做一些努力。试着去理解代码,然后我就明白了。我不是在等待答案。我也在修改这一页上的响应。忘了把它放在示例中,但是如果applicable@Iamok只是更新