英文:
Nested string fields can not be updated using reflection in an arbitrary Go struct
问题
我正在尝试使用反射在Go语言中更新结构体及其子字段的所有字符串字段。以下是一个示例代码:
package main
import (
	"fmt"
	"reflect"
	"strings"
)
func main() {
	type Inner struct {
		In1 string
		In2 []string
	}
	type Type struct {
		Name  string
		Names []string
		NewSt Inner
	}
	a := Type{
		Name:  " [(Amir[ ",
		Names: nil,
		NewSt: Inner{
			In1: " [in1",
			In2: []string{"  [in2( "},
		},
	}
	trims(&a)
	fmt.Printf("%#v\n", a)
}
func trim(str string) string {
	return strings.TrimSpace(strings.Trim(str, "[](){}, "))
}
func trims(ps interface{}) {
	v := reflect.ValueOf(ps).Elem() // Elem() dereferences pointer
	for i := 0; i < v.NumField(); i++ {
		fv := v.Field(i)
		switch fv.Kind() {
		case reflect.String:
			fv.SetString(trim(fv.String()))
		case reflect.Struct:
			in := fv.Addr().Interface()
			trims(in)
		}
	}
}
但是我遇到了panic: reflect: call of reflect.Value.Elem on struct Value错误。我该如何修复它,或者是否有更好的方法可以实现这样的功能?
谢谢。
英文:
I'm trying to update all string fields in a struct and its subfields using reflection in golang for an arbitrary struct as follows:
package main
import (
    "fmt"
    "reflect"
    "strings"
)
func main() {
    type Inner struct {
	    In1 string
	    In2 []string
    }
    type Type struct {
	    Name  string
	    Names []string
	    NewSt Inner
    }
    a := Type{
	    Name:  " [ (Amir[ ",
	    Names: nil,
	    NewSt: Inner{
		    In1: " [in1",
		    In2: []string{"  [in2( "},
	    },
    }
    trims(&a)
    fmt.Printf("%#v\n", a)
}
func trim(str string) string {
	return strings.TrimSpace(strings.Trim(str, "[](){}, "))
}
func trims(ps interface{}) {
    v := reflect.ValueOf(ps).Elem() // Elem() dereferences pointer
    for i := 0; i < v.NumField(); i++ {
	    fv := v.Field(i)
	    switch fv.Kind() {
	    case reflect.String:
		    fv.SetString(trim(fv.String()))
	    case reflect.Struct:
    		in := fv.Interface()
    		trims(&in)
    	}
    }
}
But I get panic: reflect: call of reflect.Value.Elem on struct Value error.
How can I fix it or is there any better way that I can do such thing??
Thanks.
答案1
得分: 0
func trims(ps interface{}) {
	v := reflect.ValueOf(ps)
	if v.Kind() == reflect.Ptr {
		v = v.Elem() // Elem()解引用指针
	}
	if v.Kind() != reflect.Struct {
		panic("不是结构体")
	}
	for i := 0; i < v.NumField(); i++ {
		fv := v.Field(i)
		switch fv.Kind() {
		case reflect.String:
			fv.SetString(strings.TrimSpace(fv.String()))
		case reflect.Struct:
			// 使用Addr()获取字段的可寻址值
			in := fv.Addr().Interface()
			// 不要使用&in,它会评估为*interface{},这几乎永远不是你想要的
			trims(in)
		case reflect.Slice:
			if fv.Type().Elem().Kind() == reflect.String {
				for i := 0; i < fv.Len(); i++ {
					fv.Index(i).SetString(strings.TrimSpace(fv.Index(i).String()))
				}
			}
		}
	}
}
https://go.dev/play/p/JkJTJzTckNA
英文:
func trims(ps interface{}) {
	v := reflect.ValueOf(ps)
	if v.Kind() == reflect.Ptr {
		v = v.Elem() // Elem() dereferences pointer
	}
	if v.Kind() != reflect.Struct {
		panic("not struct")
	}
	for i := 0; i < v.NumField(); i++ {
		fv := v.Field(i)
		switch fv.Kind() {
		case reflect.String:
			fv.SetString(trim(fv.String()))
		case reflect.Struct:
            // use Addr() to get an addressable
            // value of the field
			in := fv.Addr().Interface()
            // do not use &in, that evaluates
            // to *interface{}, that's almost
            // NEVER what you want
			trims(in)
        case reflect.Slice:
			if fv.Type().Elem().Kind() == reflect.String {
				for i := 0; i < fv.Len(); i++ {
					fv.Index(i).SetString(trim(fv.Index(i).String()))
				}
			}
		}
	}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论