Go语言如何读取JSON:从基础到实战
在Go语言开发中,JSON(JavaScript Object Notation)作为一种轻量级的数据交换格式,被广泛应用于API接口、配置文件、数据存储等场景,Go语言通过标准库encoding/json提供了强大的JSON处理能力,本文将详细介绍如何使用Go语言读取JSON数据,从基础结构解析到复杂场景处理,帮助你这一核心技能。
JSON与Go语言的类型映射
在读取JSON之前,需要明确JSON数据与Go语言类型之间的对应关系,这是正确解析数据的前提,官方映射规则如下:
| JSON类型 | Go语言类型 |
|---|---|
| object(对象) | map[string]T 或 struct |
| array(数组) | slice 或 array |
| string(字符串) | string |
| number(数字) | float64(默认)、int、int8等 |
| boolean(布尔) | bool |
| null(空值) | nil、interface{}、指针类型 |
理解这一映射关系后,我们就能根据JSON结构选择合适的Go类型进行定义。
基础读取:解析JSON字符串到Go结构体
最常见的需求是将JSON字符串解析为Go语言的结构化数据(如结构体),此时需要定义与JSON结构匹配的Go类型,并使用json.Unmarshal()函数完成解析。
定义结构体并使用json
假设有以下JSON数据,表示一个用户信息:
{
"name": "张三",
"age": 25,
"isStudent": false,
"courses": ["数学", "英语"],
"address": {
"city": "北京",
"district": "海淀区"
}
}
我们需要定义对应的Go结构体,并通过json标签指定JSON字段与结构体字段的映射关系(若字段名相同可省略标签):
type Address struct {
City string `json:"city"`
District string `json:"district"`
}
type User struct {
Name string `json:"name"`
Age int `json:"age"`
IsStudent bool `json:"isStudent"`
Courses []string `json:"courses"`
Address Address `json:"address"`
}
json:"field_name"标签用于处理JSON字段与Go字段名不一致的情况(如JSON中的isStudent与Go中的IsStudent驼峰命名差异)。
- 嵌套结构体(如
Address)可直接对应JSON中的嵌套对象。
使用json.Unmarshal()解析
调用json.Unmarshal()函数将JSON字符串解析到结构体实例中:
package main
import (
"encoding/json"
"fmt"
)
// 上述User和Address结构体定义...
func main() {
jsonStr := `{
"name": "张三",
"age": 25,
"isStudent": false,
"courses": ["数学", "英语"],
"address": {
"city": "北京",
"district": "海淀区"
}
}`
var user User
// 将jsonStr解析到user变量中
err := json.Unmarshal([]byte(jsonStr), &user)
if err != nil {
fmt.Println("JSON解析失败:", err)
return
}
// 输出解析结果
fmt.Printf("姓名: %s\n", user.Name)
fmt.Printf("年龄: %d\n", user.Age)
fmt.Printf("是否学生: %t\n", user.IsStudent)
fmt.Printf("课程: %v\n", user.Courses)
fmt.Printf("地址: %s%s\n", user.Address.City, user.Address.District)
}
运行结果:
姓名: 张三
年龄: 25
是否学生: false
课程: [数学 英语]
地址: 北京海淀区
json.Unmarshal()的第一个参数是[]byte类型,因此需将JSON字符串通过[]byte(jsonStr)转换;第二个参数是目标变量的指针(必须传指针,否则无法修改原变量值)。
进阶读取:解析JSON到map或interface{}
当JSON结构不固定(如动态字段、未知嵌套结构)时,可以使用map[string]interface{}或interface{}类型进行解析,通过类型断言获取具体值。
解析到map[string]interface{}
适用于解析键为字符串、值类型不固定的JSON对象:
package main
import (
"encoding/json"
"fmt"
)
func main() {
jsonStr := `{"name": "李四", "age": 30, "hobbies": ["阅读", "游泳"]}`
var data map[string]interface{}
err := json.Unmarshal([]byte(jsonStr), &data)
if err != nil {
fmt.Println("解析失败:", err)
return
}
// 通过类型断言获取字段值
if name, ok := data["name"].(string); ok {
fmt.Println("姓名:", name)
}
if age, ok := data["age"].(float64); ok { // JSON数字默认解析为float64
fmt.Println("年龄:", int(age))
}
if hobbies, ok := data["hobbies"].([]interface{}); ok {
fmt.Println("爱好:", hobbies)
}
}
运行结果:
姓名: 李四
年龄: 30
爱好: [阅读 游泳]
- 注意:JSON中的数字会被解析为
float64,若需整数类型需手动转换(如int(age))。
解析到interface{}(通用类型)
适用于任意JSON结构(对象、数组、基本类型),通过类型判断处理数据:
package main
import (
"encoding/json"
"fmt"
)
func printJSON(v interface{}) {
switch val := v.(type) {
case string:
fmt.Println("字符串:", val)
case float64:
fmt.Println("数字:", val)
case bool:
fmt.Println("布尔值:", val)
case []interface{}:
fmt.Println("数组:")
for _, item := range val {
printJSON(item)
}
case map[string]interface{}:
fmt.Println("对象:")
for key, value := range val {
fmt.Printf(" %s: ", key)
printJSON(value)
}
case nil:
fmt.Println("null")
}
}
func main() {
jsonStr := `{"name": "王五", "scores": [90, 85.5], "info": null}`
var data interface{}
json.Unmarshal([]byte(jsonStr), &data)
printJSON(data)
}
运行结果:
对象:
name: 字符串: 王五
scores: 数组:
数字: 90
数字: 85.5
info: null
interface{}类型可以存储任意JSON数据,通过类型断言(v.(type))判断具体类型,适合处理动态JSON。
处理复杂场景:动态字段、默认值与错误处理
实际开发中,JSON数据可能存在字段缺失、类型不匹配等问题,Go的json包提供了灵活的处理机制。
忽略未知字段或设置默认值
- 忽略未知字段:若JSON中有未在Go结构体中定义的字段,解析时会自动忽略,不会报错。
- 设置默认值:若JSON字段缺失,可通过结构体字段的零值作为默认值,或使用
omitempty标签(反向控制,仅当字段非零值时序列化为JSON,与解析无关),若需为缺失字段设置自定义默认值,可结合json.Unmarshaler接口实现自定义解析逻辑。
处理字段缺失或类型不匹配
使用json包解析时,若字段类型不匹配或缺失,默认会忽略并继续解析,不会报错(但字段值为零值),若需严格校验字段是否存在或类型是否正确,可通过以下方式:
- 检查字段是否存在:对于
map[string]interface{},可通过判断data["field"] != nil判断字段是否存在。
- 自定义解析逻辑:实现
json.Unmarshaler接口,为结构体字段定义自定义解析方法。
解析JSON数组
JSON数组可直接解析为Go的切片或数组,
[
{"name": "赵六", "age": 28},
{"name": "钱七", "age": 32}
]
对应的Go解析代码:
type Users []User // 定义User切片类型
func main() {
jsonStr := `[{"name": "赵六", "age": 28}, {"name": "钱七", "age": 32}]`
var users Users
json.Unmarshal([]byte(jsonStr), &users)
for _, user := range users {
fmt.Printf("%s: %d岁\n", user.Name, user.Age)
}
}
从文件或网络读取JSON
实际开发中,JSON数据常存储在
假设有以下JSON数据,表示一个用户信息:
{
"name": "张三",
"age": 25,
"isStudent": false,
"courses": ["数学", "英语"],
"address": {
"city": "北京",
"district": "海淀区"
}
}
我们需要定义对应的Go结构体,并通过json标签指定JSON字段与结构体字段的映射关系(若字段名相同可省略标签):
type Address struct {
City string `json:"city"`
District string `json:"district"`
}
type User struct {
Name string `json:"name"`
Age int `json:"age"`
IsStudent bool `json:"isStudent"`
Courses []string `json:"courses"`
Address Address `json:"address"`
}
json:"field_name"标签用于处理JSON字段与Go字段名不一致的情况(如JSON中的isStudent与Go中的IsStudent驼峰命名差异)。- 嵌套结构体(如
Address)可直接对应JSON中的嵌套对象。
使用json.Unmarshal()解析
调用json.Unmarshal()函数将JSON字符串解析到结构体实例中:
package main
import (
"encoding/json"
"fmt"
)
// 上述User和Address结构体定义...
func main() {
jsonStr := `{
"name": "张三",
"age": 25,
"isStudent": false,
"courses": ["数学", "英语"],
"address": {
"city": "北京",
"district": "海淀区"
}
}`
var user User
// 将jsonStr解析到user变量中
err := json.Unmarshal([]byte(jsonStr), &user)
if err != nil {
fmt.Println("JSON解析失败:", err)
return
}
// 输出解析结果
fmt.Printf("姓名: %s\n", user.Name)
fmt.Printf("年龄: %d\n", user.Age)
fmt.Printf("是否学生: %t\n", user.IsStudent)
fmt.Printf("课程: %v\n", user.Courses)
fmt.Printf("地址: %s%s\n", user.Address.City, user.Address.District)
}
运行结果:
姓名: 张三
年龄: 25
是否学生: false
课程: [数学 英语]
地址: 北京海淀区
json.Unmarshal()的第一个参数是[]byte类型,因此需将JSON字符串通过[]byte(jsonStr)转换;第二个参数是目标变量的指针(必须传指针,否则无法修改原变量值)。
进阶读取:解析JSON到map或interface{}
当JSON结构不固定(如动态字段、未知嵌套结构)时,可以使用map[string]interface{}或interface{}类型进行解析,通过类型断言获取具体值。
解析到map[string]interface{}
适用于解析键为字符串、值类型不固定的JSON对象:
package main
import (
"encoding/json"
"fmt"
)
func main() {
jsonStr := `{"name": "李四", "age": 30, "hobbies": ["阅读", "游泳"]}`
var data map[string]interface{}
err := json.Unmarshal([]byte(jsonStr), &data)
if err != nil {
fmt.Println("解析失败:", err)
return
}
// 通过类型断言获取字段值
if name, ok := data["name"].(string); ok {
fmt.Println("姓名:", name)
}
if age, ok := data["age"].(float64); ok { // JSON数字默认解析为float64
fmt.Println("年龄:", int(age))
}
if hobbies, ok := data["hobbies"].([]interface{}); ok {
fmt.Println("爱好:", hobbies)
}
}
运行结果:
姓名: 李四
年龄: 30
爱好: [阅读 游泳]
- 注意:JSON中的数字会被解析为
float64,若需整数类型需手动转换(如int(age))。
解析到interface{}(通用类型)
适用于任意JSON结构(对象、数组、基本类型),通过类型判断处理数据:
package main
import (
"encoding/json"
"fmt"
)
func printJSON(v interface{}) {
switch val := v.(type) {
case string:
fmt.Println("字符串:", val)
case float64:
fmt.Println("数字:", val)
case bool:
fmt.Println("布尔值:", val)
case []interface{}:
fmt.Println("数组:")
for _, item := range val {
printJSON(item)
}
case map[string]interface{}:
fmt.Println("对象:")
for key, value := range val {
fmt.Printf(" %s: ", key)
printJSON(value)
}
case nil:
fmt.Println("null")
}
}
func main() {
jsonStr := `{"name": "王五", "scores": [90, 85.5], "info": null}`
var data interface{}
json.Unmarshal([]byte(jsonStr), &data)
printJSON(data)
}
运行结果:
对象:
name: 字符串: 王五
scores: 数组:
数字: 90
数字: 85.5
info: null
interface{}类型可以存储任意JSON数据,通过类型断言(v.(type))判断具体类型,适合处理动态JSON。
处理复杂场景:动态字段、默认值与错误处理
实际开发中,JSON数据可能存在字段缺失、类型不匹配等问题,Go的json包提供了灵活的处理机制。
忽略未知字段或设置默认值
- 忽略未知字段:若JSON中有未在Go结构体中定义的字段,解析时会自动忽略,不会报错。
- 设置默认值:若JSON字段缺失,可通过结构体字段的零值作为默认值,或使用
omitempty标签(反向控制,仅当字段非零值时序列化为JSON,与解析无关),若需为缺失字段设置自定义默认值,可结合json.Unmarshaler接口实现自定义解析逻辑。
处理字段缺失或类型不匹配
使用json包解析时,若字段类型不匹配或缺失,默认会忽略并继续解析,不会报错(但字段值为零值),若需严格校验字段是否存在或类型是否正确,可通过以下方式:
- 检查字段是否存在:对于
map[string]interface{},可通过判断data["field"] != nil判断字段是否存在。 - 自定义解析逻辑:实现
json.Unmarshaler接口,为结构体字段定义自定义解析方法。
解析JSON数组
JSON数组可直接解析为Go的切片或数组,
[
{"name": "赵六", "age": 28},
{"name": "钱七", "age": 32}
]
对应的Go解析代码:
type Users []User // 定义User切片类型
func main() {
jsonStr := `[{"name": "赵六", "age": 28}, {"name": "钱七", "age": 32}]`
var users Users
json.Unmarshal([]byte(jsonStr), &users)
for _, user := range users {
fmt.Printf("%s: %d岁\n", user.Name, user.Age)
}
}
从文件或网络读取JSON
实际开发中,JSON数据常存储在



还没有评论,来说两句吧...