如何在Golang顶层结构体中获取嵌套JSON值?
如何将嵌套JSON映射到Go语言的扁平结构体
你遇到的是Go语言JSON解析里的结构体扁平化映射问题——不想用多层嵌套结构体,直接把深层的fields.description映射到顶层的Description字段,对吧?完全可行!下面给你两种实用的解决方法:
方法一:自定义UnmarshalJSON实现自定义解析
这种方法最灵活,直接在目标结构体上实现json.Unmarshaler接口,自己控制解析逻辑:
package main import ( "encoding/json" "fmt" ) // 你的目标扁平结构体 type Todo struct { Number string `json:"number"` Issues []Issue `json:"issues"` } type Issue struct { Key string `json:"key"` Self string `json:"self"` Description string // 不需要json标签,我们自己处理映射 } // 为Todo实现自定义的UnmarshalJSON方法 func (t *Todo) UnmarshalJSON(data []byte) error { // 定义中间嵌套结构体,完全匹配原始JSON的结构 type RawTodo struct { Ticket struct { Number string `json:"number"` Issues []struct { Key string `json:"key"` Self string `json:"self"` Fields struct { Description string `json:"description"` } `json:"fields"` } `json:"issues"` } `json:"ticket"` } var raw RawTodo if err := json.Unmarshal(data, &raw); err != nil { return err } // 将中间结构体的数据映射到目标扁平结构体 t.Number = raw.Ticket.Number t.Issues = make([]Issue, len(raw.Ticket.Issues)) for i, rawIssue := range raw.Ticket.Issues { t.Issues[i] = Issue{ Key: rawIssue.Key, Self: rawIssue.Self, Description: rawIssue.Fields.Description, } } return nil } func main() { rawJSON := `{ "ticket": { "number": "2", "issues": [ { "key": "TA-2", "self": "http://localhost:8080/rest/api/2/issue/10100", "fields": { "description": "This template is used to create openshift project.\n|Type|Value|Help|\n|project_name|personal_project|Enter the name of your openshift project|\n|memory|8GB|Enter the desired amount of memory|" } }, { "key": "TA-1", "self": "http://localhost:8080/rest/api/2/issue/10000", "fields": { "description": "This template is used to create openshift project.\n|Type|Value|Help|\n|project_name|my_openshift_project|Enter the name of your openshift project|\n|memory|4GB|Enter the desired amount of memory|" } } ] } }` var todo Todo err := json.Unmarshal([]byte(rawJSON), &todo) if err != nil { fmt.Println("解析错误:", err) return } // 验证结果,输出扁平化后的JSON result, _ := json.MarshalIndent(todo, "", " ") fmt.Println(string(result)) }
这种方法的核心是用中间结构体匹配原始JSON的嵌套结构,解析完成后再手动把数据映射到你想要的扁平结构体中。好处是完全按照你的需求控制字段映射,不受JSON标签的限制。
方法二:先解析到临时嵌套结构体,再转换
如果不想自定义Unmarshal方法,也可以分两步走:先把JSON解析到一个和原始结构完全一致的临时结构体,再手动转换成目标扁平结构体:
package main import ( "encoding/json" "fmt" ) // 目标扁平结构体 type Todo struct { Number string `json:"number"` Issues []Issue `json:"issues"` } type Issue struct { Key string `json:"key"` Self string `json:"self"` Description string `json:"description"` } // 临时嵌套结构体,完全匹配原始JSON的结构 type RawResponse struct { Ticket struct { Number string `json:"number"` Issues []struct { Key string `json:"key"` Self string `json:"self"` Fields struct { Description string `json:"description"` } `json:"fields"` } `json:"issues"` } `json:"ticket"` } func main() { rawJSON := `{ "ticket": { "number": "2", "issues": [ { "key": "TA-2", "self": "http://localhost:8080/rest/api/2/issue/10100", "fields": { "description": "This template is used to create openshift project.\n|Type|Value|Help|\n|project_name|personal_project|Enter the name of your openshift project|\n|memory|8GB|Enter the desired amount of memory|" } }, { "key": "TA-1", "self": "http://localhost:8080/rest/api/2/issue/10000", "fields": { "description": "This template is used to create openshift project.\n|Type|Value|Help|\n|project_name|my_openshift_project|Enter the name of your openshift project|\n|memory|4GB|Enter the desired amount of memory|" } } ] } }` var raw RawResponse err := json.Unmarshal([]byte(rawJSON), &raw) if err != nil { fmt.Println("解析错误:", err) return } // 转换为目标扁平结构体 todo := Todo{ Number: raw.Ticket.Number, Issues: make([]Issue, len(raw.Ticket.Issues)), } for i, item := range raw.Ticket.Issues { todo.Issues[i] = Issue{ Key: item.Key, Self: item.Self, Description: item.Fields.Description, } } // 输出结果 result, _ := json.MarshalIndent(todo, "", " ") fmt.Println(string(result)) }
这种方法更直观,适合简单的场景。先完成原始JSON的解析,再做一次数据拷贝,把深层字段提取到顶层结构体中。
为什么你的原始写法无效?
Go标准库的encoding/json并不支持用json:"field.description"这种点符号来映射深层嵌套字段——它只能直接匹配当前层级的键名,所以这种写法是行不通的,必须通过上面的两种方法来处理。
内容的提问来源于stack exchange,提问作者Johann




