Go 如何将嵌套结构中的字段设置为零值?
假设我有一个structGo 如何将嵌套结构中的字段设置为零值?,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"` }
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只是更新