使用structtag包修改Go结构体Tag后无法保留修改值求助
问题:修改Go结构体字段Tag后无法在外部保留
以下是我编写的Go代码:
package main import ( "fmt" "reflect" "github.com/fatih/structtag" ) type Person struct { Name string `json:"name"` Age int `json:"age"` Address string `json:"address"` } func main() { p := &Person{ Name: "John", Age: 30, Address: "123 Main St", } p.setTag("Name","newtag") p.read1Tag ("Name") } func (p* Person) setTag(fieldName string,tagValue string){ field, _ := reflect.TypeOf(*p).FieldByName(fieldName) tags, _ := structtag.Parse(string(field.Tag)) tags.Delete("json") tag := &structtag.Tag{ Key: "json", Name: tagValue, } tags.Set(tag) field.Tag = reflect.StructTag(tags.String()) tag1, _ := tags.Get("json") fmt.Println (tag1) field.Tag = reflect.StructTag(tags.String()) } func (p Person) read1Tag (fieldName string) { field, _ := reflect.TypeOf(p).FieldByName(fieldName) tags, _ := structtag.Parse(string(field.Tag)) tag, _ := tags.Get("json") fmt.Println (tag) }
我想要修改Person结构体中Name字段的json tag,代码在setTag函数内成功修改了tag,但从main函数中读取时仍然显示旧的tag(我已在两个函数内都打印了tag)。我已经使用了指针接收器,请问如何让修改后的tag在函数外也能保留?
解决方案
问题核心在于:Go语言中结构体的类型信息(包括字段Tag)是编译期确定的全局只读数据。你在setTag里通过reflect.TypeOf(*p).FieldByName拿到的field是类型信息的副本,修改这个副本的Tag字段只会影响当前副本,不会改变原结构体类型的定义,所以外部读取时还是原来的Tag。
要实现动态修改Tag并在外部生效的效果,不能直接修改结构体类型的Tag,而是需要自己维护动态Tag的映射,并在需要使用Tag的逻辑中优先使用这个映射。以下是两种可行方案:
方案1:给结构体添加自定义Tag映射字段
在Person结构体中新增成员变量存储动态修改的Tag,读写时优先使用这个映射:
package main import ( "fmt" "reflect" "github.com/fatih/structtag" ) type Person struct { Name string `json:"name"` Age int `json:"age"` Address string `json:"address"` customTags map[string]string // 存储动态Tag:键为字段名,值为json tag值 } func main() { p := &Person{ Name: "John", Age: 30, Address: "123 Main St", customTags: make(map[string]string), } p.setTag("Name","newtag") p.read1Tag("Name") } func (p* Person) setTag(fieldName string, tagValue string){ // 先验证字段是否存在 if _, ok := reflect.TypeOf(*p).FieldByName(fieldName); ok { p.customTags[fieldName] = tagValue fmt.Println(&structtag.Tag{Key: "json", Name: tagValue}) } } func (p Person) read1Tag(fieldName string) { // 优先读取自定义映射的Tag if val, ok := p.customTags[fieldName]; ok { fmt.Println(&structtag.Tag{Key: "json", Name: val}) return } // 无自定义Tag时读取原结构体Tag field, _ := reflect.TypeOf(p).FieldByName(fieldName) tags, _ := structtag.Parse(string(field.Tag)) tag, _ := tags.Get("json") fmt.Println(tag) }
方案2:自定义JSON序列化逻辑(针对JSON场景)
如果修改Tag是为了影响JSON序列化/反序列化,可以自定义MarshalJSON方法,在序列化时使用动态Tag:
package main import ( "encoding/json" "fmt" "reflect" "github.com/fatih/structtag" ) type Person struct { Name string `json:"name"` Age int `json:"age"` Address string `json:"address"` customTags map[string]string } func main() { p := &Person{ Name: "John", Age: 30, Address: "123 Main St", customTags: make(map[string]string), } p.setTag("Name","newtag") p.read1Tag("Name") // 测试JSON序列化 data, _ := json.Marshal(p) fmt.Println(string(data)) // 输出 {"newtag":"John","age":30,"address":"123 Main St"} } func (p* Person) setTag(fieldName string, tagValue string){ if _, ok := reflect.TypeOf(*p).FieldByName(fieldName); ok { p.customTags[fieldName] = tagValue fmt.Println(&structtag.Tag{Key: "json", Name: tagValue}) } } func (p Person) read1Tag(fieldName string) { if val, ok := p.customTags[fieldName]; ok { fmt.Println(&structtag.Tag{Key: "json", Name: val}) return } field, _ := reflect.TypeOf(p).FieldByName(fieldName) tags, _ := structtag.Parse(string(field.Tag)) tag, _ := tags.Get("json") fmt.Println(tag) } // 自定义MarshalJSON方法,使用动态Tag生成JSON字段 func (p Person) MarshalJSON() ([]byte, error) { result := make(map[string]interface{}) t := reflect.TypeOf(p) v := reflect.ValueOf(p) for i := 0; i < t.NumField(); i++ { field := t.Field(i) // 跳过自定义Tag字段 if field.Name == "customTags" { continue } // 获取JSON键:优先用自定义Tag,否则用原结构体Tag jsonKey := field.Tag.Get("json") if customKey, ok := p.customTags[field.Name]; ok { jsonKey = customKey } result[jsonKey] = v.Field(i).Interface() } return json.Marshal(result) }
内容的提问来源于stack exchange,提问作者Castracucchi Davide
相关产品推荐
相关产品推荐

