go常用包json

  • 序列化(Marshal)​:将 Go 的数据结构转换为 JSON 字符串。
  • 反序列化(Unmarshal)​:将 JSON 字符串转换为 Go 的数据结构。

使用 json.Marshal 进行序列化

json.Marshal 函数可以将 Go 的数据结构转换为 JSON 格式的字节切片([]byte

go 复制代码
package main

import (
    "encoding/json"
    "fmt"
    "log"
)

// 定义一个结构体
type Person struct {
    Name   string `json:"name"`
    Age    int    `json:"age"`
    Height float64 `json:"height,omitempty"` // omitempty 表示如果字段为空则忽略
    Address string `json:"-"`  //序列化时忽略该字段
}

func main() {
    // 创建一个 Person 实例
    person := Person{
        Name: "张三",
        Age:  30,
        // Height 未设置,序列化时将被忽略
        Address: "北京", // 由于标签 `json:"-"`序列化时忽略该字段
    }

    // 序列化为 JSON
    jsonData, err := json.Marshal(person)
    if err != nil {
        log.Fatalf("JSON 序列化失败: %v", err)
    }

    // 输出 JSON 字符串
    fmt.Println(string(jsonData))
}

输出结果

json 复制代码
{"name":"张三","age":30}

解释:​

  • NameAge 字段被成功序列化为 JSON。
  • Height 字段由于未设置值,并且有 omitempty 标签,因此在 JSON 中被忽略。
  • Address 字段由于标签 json:"-"
    `,表示在序列化时忽略该字段。

-----json.Unmarshal 进行反序列化

json.Unmarshal 函数可以将 JSON 数据解析并填充到 Go 的数据结构中。其函数签名如下:

go 复制代码
func Unmarshal(data []byte, v interface{}) error
  • data:要反序列化的 JSON 数据,类型为 []byte
  • v:指向要填充数据的 Go 数据结构的指针。
  • 返回值:如果反序列化成功,返回 nil;否则,返回相应的错误信息。
go 复制代码
package main

import (
    "encoding/json"
    "fmt"
    "log"
)

type Person struct {
    Name    string  `json:"name"`
    Age     int     `json:"age"`
    Height  float64 `json:"height,omitempty"`
    Address string  `json:"-"`
}

func main() {
    // JSON 数据
    jsonData := `{
        "name": "张三",
        "age": 30,
        "height": 175.5
    }`

    // 创建一个 Person 实例
    var person Person

    // 反序列化 JSON 数据到 person 结构体
    err := json.Unmarshal([]byte(jsonData), &person)
    if err != nil {
        log.Fatalf("JSON 反序列化失败: %v", err)
    }

    // 输出结果
    fmt.Printf("姓名: %s, 年龄: %d, 身高: %.1f\n", person.Name, person.Age, person.Height)
}

输出结果

makefile 复制代码
姓名: 张三, 年龄: 30, 身高: 175.5

解释:​

  • NameAge 字段成功从 JSON 中提取并赋值给结构体字段。
  • Height 字段根据 JSON 数据被赋值为 175.5
  • Address 字段由于标签 json:"-"
    `,在反序列化时被忽略。

控制 JSON 输出

使用结构体标签(Struct Tags)

结构体标签可以控制字段JSON 中的名称、是否忽略等。例如:

go 复制代码
type Person struct {
    FirstName string `json:"first_name"`
    LastName  string `json:"last_name"`
    Age       int    `json:"-"`
}
  • json:"first_name":将字段序列化为 first_name
  • json:"-"
    `:忽略该字段,不进行序列化。

处理布尔值和数字

Go 的布尔值和数字类型会自动转换为 JSON 中的 true/false 和数值。

css 复制代码
type Status struct {
    Active bool    `json:"active"`
    Score  float64 `json:"score"`
}

status := Status{
    Active: true,
    Score:  95.5,
}

jsonData, _ := json.Marshal(status)
fmt.Println(string(jsonData)) // {"active":true,"score":95.5}

处理数组和切片

Go 的数组和切片会被序列化为 JSON 数组。

css 复制代码
type Scores struct {
    Name   string  `json:"name"`
    Scores []float64 `json:"scores"`
}

scores := Scores{
    Name:   "李四",
    Scores: []float64{85.0, 90.5, 78.0},
}

jsonData, _ := json.Marshal(scores)
fmt.Println(string(jsonData)) // {"name":"李四","scores":[85,90.5,78]}

处理嵌套结构体

可以在 Go 结构体中嵌套其他结构体,JSON 也会相应嵌套。

css 复制代码
type Address struct {
    City  string `json:"city"`
    State string `json:"state"`
}

type Person struct {
    Name    string  `json:"name"`
    Age     int     `json:"age"`
    Address Address `json:"address"`
}

person := Person{
    Name: "王五",
    Age:  25,
    Address: Address{
        City:  "上海",
        State: "上海",
    },
}

jsonData, _ := json.Marshal(person)
fmt.Println(string(jsonData)) 
// {"name":"王五","age":25,"address":{"city":"上海","state":"上海"}}

自定义 JSON 输出

有时候默认的序列化行为不能满足需求,可以通过实现 json.Marshaler 接口来自定义序列化逻辑。

实现 json.Marshaler 接口

go 复制代码
type CustomDate struct {
    Year  int
    Month int
    Day   int
}

func (c CustomDate) MarshalJSON() ([]byte, error) {
    return []byte(fmt.Sprintf(""%d-%02d-%02d"", c.Year, c.Month, c.Day)), nil
}

type Event struct {
    Name string     `json:"name"`
    Date CustomDate `json:"date"`
}

event := Event{
    Name: "会议",
    Date: CustomDate{Year: 2023, Month: 10, Day: 1},
}

jsonData, _ := json.Marshal(event)
fmt.Println(string(jsonData)) // {"name":"会议","date":"2023-10-01"}

处理指针和空值

使用指针可以让字段在序列化时有条件地包含或不包含。

css 复制代码
type User struct {
    Name    string  `json:"name"`
    Age     int     `json:"age"`
    Email   *string `json:"email,omitempty"` // 如果 Email 为 nil,则字段被忽略
}

user := User{
    Name: "赵六",
    Age:  28,
    // Email 未设置,序列化时将被忽略
}

jsonData, _ := json.Marshal(user)
fmt.Println(string(jsonData)) // {"name":"赵六","age":28}

// 设置 Email
email := "zhaoliu@example.com"
user.Email = &email

jsonData, _ = json.Marshal(user)
fmt.Println(string(jsonData)) // {"name":"赵六","age":28,"email":"zhaoliu@example.com"}

错误处理

在实际使用中,应该总是检查 json.Marshal 返回的错误,以确保序列化过程没有问题。

go 复制代码
jsonData, err := json.Marshal(person)
if err != nil {
    log.Fatalf("JSON 序列化失败: %v", err)
}

常见的错误包括:

  • 结构体字段不可导出(首字母小写)。
  • 循环引用(Go 的 encoding/json 不支持循环结构体的序列化)。

完整示例

以下是一个综合示例,展示了多种序列化场景:

go 复制代码
package main

import (
    "encoding/json"
    "fmt"
    "log"
)

type Address struct {
    City  string `json:"city"`
    State string `json:"state"`
}

type Person struct {
    FirstName string        `json:"first_name"`
    LastName  string        `json:"last_name"`
    Age       int           `json:"age,omitempty"`
    Height    float64       `json:"height,omitempty"`
    Address   Address       `json:"address,omitempty"`
    Tags      []string      `json:"tags,omitempty"`
    Birthday  *CustomDate   `json:"birthday,omitempty"`
}

type CustomDate struct {
    Year  int
    Month int
    Day   int
}

func (c CustomDate) MarshalJSON() ([]byte, error) {
    return []byte(fmt.Sprintf(""%d-%02d-%02d"", c.Year, c.Month, c.Day)), nil
}

func main() {
    person := Person{
        FirstName: "孙",
        LastName:  "七",
        Age:       32,
        Address: Address{
            City:  "广州",
            State: "广东",
        },
        Tags: []string{"developer", "gopher"},
        Birthday: &CustomDate{
            Year:  1991,
            Month: 5,
            Day:   20,
        },
    }

    jsonData, err := json.MarshalIndent(person, "", "  ")
    if err != nil {
        log.Fatalf("JSON 序列化失败: %v", err)
    }

    fmt.Println(string(jsonData))
}

输出结果

json 复制代码
{
  "first_name": "孙",
  "last_name": "七",
  "age": 32,
  "address": {
    "city": "广州",
    "state": "广东"
  },
  "tags": [
    "developer",
    "gopher"
  ],
  "birthday": "1991-05-20"
}

说明:​

  • 使用 json.MarshalIndent 可以生成格式化的 JSON,便于阅读。
  • 所有导出的字段根据标签和值被正确序列化。

-----------------反序列化

使用 map[string]interface{} 处理动态 JSON

有时候,JSON 的结构在编译时未知,可以使用 map[string]interface{} 来处理动态 JSON 数据。

csharp 复制代码
func main() {
    jsonData := `{
        "name": "孙七",
        "age": 32,
        "hobbies": ["reading", "gaming", "coding"],
        "address": {
            "city": "广州",
            "state": "广东"
        }
    }`

    var result map[string]interface{}
    err := json.Unmarshal([]byte(jsonData), &result)
    if err != nil {
        log.Fatalf("JSON 反序列化失败: %v", err)
    }

    fmt.Println("姓名:", result["name"])
    fmt.Println("年龄:", result["age"])

    // 处理切片
    hobbies, ok := result["hobbies"].([]interface{})
    if ok {
        fmt.Print("爱好: ")
        for _, hobby := range hobbies {
            fmt.Printf("%v ", hobby)
        }
        fmt.Println()
    }

    // 处理嵌套的 map
    address, ok := result["address"].(map[string]interface{})
    if ok {
        fmt.Printf("城市: %v, 州: %v\n", address["city"], address["state"])
    }
}

输出结果

makefile 复制代码
姓名: 孙七
年龄: 32
爱好: reading gaming coding 
城市: 广州, 州: 广东

注意:​ 使用 map[string]interface{} 时,所有的值都是 interface{} 类型,需要通过类型断言将其转换为具体的类型。

自定义反序列化逻辑

有时候,默认的反序列化行为不能满足需求,可以通过实现 json.Unmarshaler 接口来自定义反序列化逻辑。

实现 json.Unmarshaler 接口

go 复制代码
type CustomDate struct {
    Year  int
    Month int
    Day   int
}

func (c *CustomDate) UnmarshalJSON(data []byte) error {
    // 假设 JSON 中的日期格式为 "YYYY-MM-DD"
    var s string
    if err := json.Unmarshal(data, &s); err != nil {
        return err
    }
    // 解析字符串日期
    _, err := fmt.Sscanf(s, "%d-%d-%d", &c.Year, &c.Month, &c.Day)
    return err
}

type Event struct {
    Name     string      `json:"name"`
    Date     CustomDate  `json:"date"`
}

func main() {
    jsonData := `{"name":"会议","date":"2023-10-01"}`

    var event Event
    err := json.Unmarshal([]byte(jsonData), &event)
    if err != nil {
        log.Fatalf("JSON 反序列化失败: %v", err)
    }

    fmt.Printf("事件: %s, 日期: %d-%02d-%02d\n", event.Name, event.Date.Year, event.Date.Month, event.Date.Day)
}

输出结果

makefile 复制代码
事件: 会议, 日期: 2023-10-01

错误处理

在实际使用中,应该总是检查 json.Unmarshal 返回的错误,以确保反序列化过程没有问题。

go 复制代码
type User  struct {
    Name    string  `json:"name"`
    Age     int     `json:"age"`
}

jsonData := `{"name":"赵六","age":"二十八"}` // age 应为数字

var user User
err := json.Unmarshal([]byte(jsonData), &user)
if err != nil {
    log.Fatalf("JSON 反序列化失败: %v", err)
}

上述代码会因为 age 字段类型不匹配而导致反序列化失败,并输出相应的错误信息。

综合示例

csharp 复制代码
package main

import (
    "encoding/json"
    "fmt"
    "log"
)

type Address struct {
    City  string `json:"city"`
    State string `json:"state"`
}

type Person struct {
    FirstName string        `json:"first_name"`
    LastName  string        `json:"last_name"`
    Age       int           `json:"age,omitempty"`
    Height    float64       `json:"height,omitempty"`
    Address   Address       `json:"address,omitempty"`
    Tags      []string      `json:"tags,omitempty"`
    Birthday  *CustomDate   `json:"birthday,omitempty"`
}

type CustomDate struct {
    Year  int
    Month int
    Day   int
}

func (c *CustomDate) UnmarshalJSON(data []byte) error {
    var s string
    if err := json.Unmarshal(data, &s); err != nil {
        return err
    }
    _, err := fmt.Sscanf(s, "%d-%d-%d", &c.Year, &c.Month, &c.Day)
    return err
}

func main() {
    jsonData := `{
        "first_name": "孙",
        "last_name": "七",
        "age": 32,
        "height": 175.5,
        "address": {"city":"广州","state":"广东"},
        "tags": ["developer", "gopher"],
        "birthday": "1991-05-20"
    }`

    var person Person
    err := json.Unmarshal([]byte(jsonData), &person)
    if err != nil {
        log.Fatalf("JSON 反序列化失败: %v", err)
    }

    fmt.Printf("姓名: %s %s, 年龄: %d, 身高: %.1f\n", person.FirstName, person.LastName, person.Age, person.Height)
    fmt.Printf("地址: %s, %s\n", person.Address.City, person.Address.State)
    fmt.Printf("标签: %v\n", person.Tags)
    if person.Birthday != nil {
        fmt.Printf("生日: %d-%02d-%02d\n", person.Birthday.Year, person.Birthday.Month, person.Birthday.Day)
    }
}

输出结果

makefile 复制代码
姓名: 孙 七, 年龄: 32, 身高: 175.5
地址: 广州, 广东
标签: [developer gopher]
生日: 1991-05-20

相关推荐
lekami_兰7 小时前
MySQL 长事务:藏在业务里的性能 “隐形杀手”
数据库·mysql·go·长事务
却尘10 小时前
一篇小白也能看懂的 Go 字符串拼接 & Builder & cap 全家桶
后端·go
ん贤11 小时前
一次批量删除引发的死锁,最终我选择不加锁
数据库·安全·go·死锁
mtngt111 天前
AI DDD重构实践
go
Grassto2 天前
12 go.sum 是如何保证依赖安全的?校验机制源码解析
安全·golang·go·哈希算法·go module
Grassto4 天前
11 Go Module 缓存机制详解
开发语言·缓存·golang·go·go module
程序设计实验室5 天前
2025年的最后一天,分享我使用go语言开发的电子书转换工具网站
go
我的golang之路果然有问题5 天前
使用 Hugo + GitHub Pages + PaperMod 主题 + Obsidian 搭建开发博客
golang·go·github·博客·个人开发·个人博客·hugo
啊汉7 天前
古文观芷App搜索方案深度解析:打造极致性能的古文搜索引擎
go·软件随想
asaotomo8 天前
一款 AI 驱动的新一代安全运维代理 —— DeepSentry(深哨)
运维·人工智能·安全·ai·go