10.1go常用包json

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

使用 json.Marshal 进行序列化

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

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))
}

输出结果

{"name":"张三","age":30}

解释:​

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

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

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

func Unmarshal(data []byte, v interface{}) error
  • data:要反序列化的 JSON 数据,类型为 []byte
  • v:指向要填充数据的 Go 数据结构的指针。
  • 返回值:如果反序列化成功,返回 nil;否则,返回相应的错误信息。
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)
}

输出结果

姓名: 张三, 年龄: 30, 身高: 175.5

解释:​

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

控制 JSON 输出

使用结构体标签(Struct Tags)

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

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 和数值。

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 数组。

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 也会相应嵌套。

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 接口

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"}

处理指针和空值

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

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 := "[email protected]"
user.Email = &email

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

错误处理

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

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

常见的错误包括:

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

完整示例

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

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))
}

输出结果

{
  "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 数据。

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"])
    }
}

输出结果

姓名: 孙七
年龄: 32
爱好: reading gaming coding 
城市: 广州, 州: 广东

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

自定义反序列化逻辑

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

实现 json.Unmarshaler 接口

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)
}

输出结果

事件: 会议, 日期: 2023-10-01

错误处理

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

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 字段类型不匹配而导致反序列化失败,并输出相应的错误信息。

综合示例

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)
    }
}

输出结果

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

你可能感兴趣的:(go语言基础,golang,json,开发语言)