Go新手求教:如何简化嵌套结构体的JSON反序列化
优化Go语言JSON到嵌套结构体的反序列化实现
问题背景
现有如下JSON负载:
{ "name": "foo-bar", "profileName": "my-profile-name" }
尝试用以下嵌套结构体进行反序列化:
type Project struct { Name string `json:"name"` Profile *Profile `json:"profileName,omitempty"` } type Profile struct { // Name is the name of the namespace created for the project Name string `json:"profileName"` }
直接反序列化会失败,因为JSON中profileName是字符串类型,而结构体中Profile是结构体指针类型。自己实现了一个UnmarshalJSON方法可以解决问题,但代码过于繁琐,希望找到更简洁的实现方式。
现有解决方案
package main import ( "encoding/json" "fmt" ) type Project struct { Name string `json:"name"` Profile *Profile `json:"profileName,omitempty"` } type Profile struct { // Name is the name of the namespace created for the project Name string `json:"profileName"` } func (p *Profile) UnmarshalJSON(data []byte) error { // try to unmarshal as a string first var s string err := json.Unmarshal(data, &s) if err == nil { p.Name = s return nil } // otherwise unmarshal as a struct type Alias Profile aux := &struct { *Alias }{ Alias: (*Alias)(p), } if err := json.Unmarshal(data, &aux); err != nil { return err } *p = *(*Profile)(aux.Alias) return nil } func main() { payload := []byte(`{ "name": "foo-bar", "profileName": "my-profile-name" }`) project := Project{ Profile: &Profile{}, } err := json.Unmarshal(payload, &project) if err != nil { panic(err) } fmt.Printf("Name: %s\nProfile Name: %s", project.Name, project.Profile.Name) }
更优实现方式
方式一:简化自定义UnmarshalJSON方法
如果只需要兼容字符串类型的profileName,可以直接简化Profile的反序列化方法:
func (p *Profile) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { return err } p.Name = s return nil }
如果需要同时兼容字符串和结构体形式的profileName(比如JSON中可能出现"profileName": {"profileName": "xxx"}),可以优化为:
func (p *Profile) UnmarshalJSON(data []byte) error { // 先尝试解析为字符串 var s string if err := json.Unmarshal(data, &s); err == nil { p.Name = s return nil } // 解析为结构体形式 var aux struct { Name string `json:"profileName"` } if err := json.Unmarshal(data, &aux); err != nil { return err } p.Name = aux.Name return nil }
方式二:使用中间字段转换
不需要自定义UnmarshalJSON,而是在Project结构体中添加一个中间字段接收字符串,再手动赋值:
type Project struct { Name string `json:"name"` ProfileStr string `json:"profileName"` Profile *Profile `json:"-"` // 忽略JSON序列化/反序列化 } func main() { payload := []byte(`{ "name": "foo-bar", "profileName": "my-profile-name" }`) var project Project err := json.Unmarshal(payload, &project) if err != nil { panic(err) } // 手动赋值给Profile project.Profile = &Profile{Name: project.ProfileStr} fmt.Printf("Name: %s\nProfile Name: %s", project.Name, project.Profile.Name) }
方式三:使用自定义字符串类型
如果Profile本质上只需要存储一个字符串,可以直接将Profile定义为自定义字符串类型,简化实现:
type Profile string func (p *Profile) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { return err } *p = Profile(s) return nil } type Project struct { Name string `json:"name"` Profile *Profile `json:"profileName,omitempty"` } func main() { payload := []byte(`{ "name": "foo-bar", "profileName": "my-profile-name" }`) var project Project err := json.Unmarshal(payload, &project) if err != nil { panic(err) } fmt.Printf("Name: %s\nProfile Name: %s", project.Name, *project.Profile) }
内容的提问来源于stack exchange,提问作者G.M
相关产品推荐
相关产品推荐

