我正在尝试创建一个函数,让我可以通过名称动态设置任意结构的值。例如。给定Page
嵌入式Link
结构,我希望能够将page.Link.Url
设置为新字符串。
这是我的代码以及失败的测试:
func TestSetFieldSubFieldByName(t *testing.T) {
page := APage {
"page title",
ALink {
"link title",
"http://www.example.com",
},
}
newPageTitle := "New page title"
setFieldSubFieldByName(&page, "Title", newPageTitle)
if page.Title != newPageTitle {
t.Errorf("Expected %s != %s", newPageTitle, page.Title)
}
newUrl := "http://new.example.com"
setFieldSubFieldByName(&page, "Link.Url", newUrl)
if page.Link.Url != newUrl {
t.Errorf("Expected %s != %s", newUrl, page.Link.Url)
}
}
type ALink struct {
Title string
Url string
}
type APage struct {
Title string
Link ALink
}
// Sets the value of a struct's field by name
func setFieldSubFieldByName(object interface{}, inputFieldName string, newValue string) {
log.Printf("Will try to set field '%s' to '%s' on %#v (type %T)", inputFieldName, newValue, object, object)
reflectedValues := reflect.ValueOf(object)
fieldNameParts := strings.Split(inputFieldName, ".")
fieldName := fieldNameParts[0]
// struct
reflectedElem := reflectedValues.Elem()
if reflectedElem.Kind() == reflect.Struct {
// exported field
field := reflectedElem.FieldByName(fieldName)
log.Printf("fieldByName returned %#v for field '%s'", field, fieldName)
if field.IsValid() {
// A Value can be changed only if it is addressable and was not obtained by the use of unexported struct fields.
if field.CanSet() {
// change value of N
if field.Kind() == reflect.String {
field.SetString(newValue)
log.Printf("Field '%s' successfully updated to '%s'", fieldName, newValue)
} else if field.Kind() == reflect.Struct && len(fieldNameParts) > 0 {
log.Printf("Trying to set subfield '%s' to '%s'", fieldNameParts[1], newValue)
subObj := field.Interface()
setFieldSubFieldByName(subObj, fieldNameParts[1], newValue)
}
}
}
}
}
它成功设置了page.Title
的值,但它没有尝试在嵌入式结构上设置值。我认为问题在于我最初调用函数传递对page
的引用,但是当我递归调用setFieldSubFieldByName
时,它传递的是实际对象,而不是指向它的指针。
有没有办法在递归之前获得对嵌入式结构的引用,或者是否有一些检查我可以在函数的开头执行以查明我是在查看实际实例还是只是指针?
我该如何解决这个问题?