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

相关推荐
梁梁梁梁较瘦3 小时前
边界检查消除(BCE,Bound Check Elimination)
go
梁梁梁梁较瘦4 小时前
指针
go
梁梁梁梁较瘦4 小时前
内存申请
go
半枫荷4 小时前
七、Go语法基础(数组和切片)
go
梁梁梁梁较瘦21 小时前
Go工具链
go
半枫荷1 天前
六、Go语法基础(条件控制和循环控制)
go
半枫荷2 天前
五、Go语法基础(输入和输出)
go
小王在努力看博客2 天前
CMS配合闲时同步队列,这……
go
Anthony_49263 天前
逻辑清晰地梳理Golang Context
后端·go
Dobby_054 天前
【Go】C++ 转 Go 第(二)天:变量、常量、函数与init函数
vscode·golang·go