如何通过反射为嵌套结构体设置新的Tag值
问题描述
用户定义了以下结构体:
type Library struct { Book Book Owner Owner Editorial Editorial } type Book struct { ID string `json:"id"` Title string `json:"title"` Description string `json:"description"` Category string `json:"category"` }
需求是修改自动生成的Book结构体中Category字段的JSON标签为"categoryID",但在代码中修改sub变量的标签后,不知道如何将其赋值给原字段f,以下是用户的尝试代码:
func renameTags(p interface{}) any { rv := reflect.ValueOf(p) rt := rv.Elem().Type() fields := make([]reflect.StructField, rt.NumField()) for i := range fields { f := rt.Field(i) if f.Type.Kind() == reflect.Struct && f.Type.Name() == "Book" { sub := f.Type.Field(13) sub.Tag = reflect.StructTag(`json:"` + "categoryID" + `"`) } fields[i] = f } st := reflect.ValueOf(fields) x := st.Interface() return x }
解决方案
问题分析
- 直接通过索引(如
13)获取Book字段不可靠,Book结构体中Category是第4个字段,索引应为3,且字段顺序可能变动,建议用字段名定位。 reflect.StructField是值类型,修改sub的标签不会影响原f.Type中的字段,必须重新构建整个结构体类型才能生效。- 用户代码仅修改了字段副本,未将修改后的字段整合回原结构体类型,也未生成可使用的新实例。
修正后的代码
func renameTags(p interface{}) interface{} { rv := reflect.ValueOf(p).Elem() rt := rv.Type() newLibFields := make([]reflect.StructField, rt.NumField()) for i := range newLibFields { libField := rt.Field(i) // 定位Library中的Book类型字段 if libField.Type.Name() == "Book" && libField.Type.Kind() == reflect.Struct { bookType := libField.Type newBookFields := make([]reflect.StructField, bookType.NumField()) // 遍历Book的所有字段,找到Category并修改标签 for j := range newBookFields { bookField := bookType.Field(j) if bookField.Name == "Category" { // 复制字段并修改JSON标签 modifiedBookField := bookField modifiedBookField.Tag = reflect.StructTag(`json:"categoryID"`) newBookFields[j] = modifiedBookField } else { newBookFields[j] = bookField } } // 创建新的Book结构体类型 newBookType := reflect.StructOf(newBookFields) // 复制Library的字段并替换其类型为新的Book类型 modifiedLibField := libField modifiedLibField.Type = newBookType newLibFields[i] = modifiedLibField } else { newLibFields[i] = libField } } // 创建新的Library结构体类型 newLibType := reflect.StructOf(newLibFields) // 生成新类型的实例并复制原数据 newInstance := reflect.New(newLibType).Elem() newInstance.Set(rv) return newInstance.Interface() }
关键说明
- 用字段名
"Category"定位目标字段,避免因结构体字段顺序变更导致的错误。 - 通过
reflect.StructOf重新构建修改后的Book和Library结构体类型,因为原类型是不可变的。 - 最后生成新类型的实例并复制原数据,确保可以直接使用该实例进行JSON序列化,得到修改标签后的结果。
内容的提问来源于stack exchange,提问作者mrTarragona
相关产品推荐
相关产品推荐

