Go语言中json操作的实现

Written by

in

文章目录
  • 理解 JSON 类型与 Go 类型的映射是处理 JSON 的基础: JSON 类型 Go 类型 说明 对象 (object) struct, map[string]T 键值对集合 数组 (array) []T (切片), [n]T (数组) 值的有序集合,通常使用切片更灵活 字符串 (string) string 数字 (number) int, float64 等 注意:JSON 中的数字默认解码为 float64 布尔 (boolean) bool null nil
  • Go 使用标准库 encoding/json 进行 JSON 处理。 编码 (序列化,Marshal):使用 json.Marshal 将 Go 数据结构转换为 JSON 字节切片 ([]byte)。 package main import ( “encoding/json” “fmt” ) type Person struct { Name string `json:”name”` // 通过标签指定JSON字段名 Age int `json:”age”` } func main() { p := Person{Name: “Alice”, Age: 30} jsonBytes, err := json.Marshal(p) if err != nil { panic(err) } fmt.Println(string(jsonBytes)) // 输出:{“name”:”Alice”,”age”:30} } 解码 (反序列化,Unmarshal):使用 json.Unmarshal 将 JSON 字节切片解析回 Go 数据结构。 jsonStr := `{“name”:”Bob”,”age”:25}` var p2 Person err = json.Unmarshal([]byte(jsonStr), &p2) // 注意传递指针 if err != nil { panic(err) } fmt.Printf(“%+vn”, p2) // 输出:{Name:Bob Age:25} 格式化输出:使用 json.MarshalIndent 生成带缩进的格式化 JSON,便于阅读。 jsonBytes, err := json.MarshalIndent(p, “”, ” “) // 前缀为空,缩进为两个空格 if err != nil { panic(err) } fmt.Println(string(jsonBytes)) /* 输出: { “name”: “Alice”, “age”: 30 } */
  • 结构体标签是控制 JSON 序列化和反序列化行为的关键。 自定义字段名:使用 json:"field_name" 指定 JSON 字段名。 type Product struct { ProductID int `json:”id”` // JSON中字段名为 “id” Name string `json:”name”` // JSON中字段名为 “name” } 忽略字段: type User struct { Name string `json:”name”` Password string `json:”-“` // 序列化时忽略密码字段 } type User struct { Name string `json:”name”` Email string `json:”email,omitempty”` // 如果Email为空,则序列化时不包含此字段 Age int `json:”age,omitempty”` // 如果Age为0,则序列化时不包含此字段 } 永久忽略:使用 json:"-",该字段不会参与 JSON 序列化。 条件忽略(空值):使用 omitempty,当字段为零值时(如空字符串、零值、nil指针等),序列化时省略该字段。 字符串形式的数字:有时需要将数字类型以字符串形式序列化,或从字符串形式的数字反序列化,可以使用 ,string 标签选项。 type MyData struct { Num int `json:”num,string”` // 序列化为 {“num”: “123”}, 从 {“num”: “123”} 反序列化 } 注意:此标签仅适用于 int, float, bool 等类型与字符串形式的互转,且 JSON 中对应的值必须是带引号的字符串。
  • 当你无法预知 JSON 的确切结构时,有两种主要方法: 使用 map[string]interface{}:适合快速访问,但需要类型断言,安全性较低。 jsonStr := `{“name”:”Alice”,”age”:30,”hobbies”:[“reading”,”traveling”]}` var data map[string]interface{} err := json.Unmarshal([]byte(jsonStr), &data) if err != nil { panic(err) } name := data[“name”].(string) // 类型断言 age := data[“age”].(float64) // JSON数字默认转为float64 hobbies := data[“hobbies”].([]interface{}) fmt.Println(name, age, hobbies[0]) 使用 json.RawMessage:延迟解析,允许你先处理已知部分,再按需解析未知部分,更灵活安全。 type Message struct { Name string `json:”name”` Extra json.RawMessage `json:”extra”` // 原始JSON字节,暂不解析 } var msg Message json.Unmarshal([]byte(jsonStr), &msg) // 之后根据条件再解析 Extra 字段
  • time.Time 类型默认序列化为 RFC 3339 格式的字符串。你也可以为 time.Time 定义自定义的 Marshaler 和 Unmarshaler 接口实现,以适应特定的时间格式。 type Event struct { Name string `json:”name”` Timestamp time.Time `json:”timestamp”` // 默认格式:2006-01-02T15:04:05Z07:00 } event := Event{Name: “Party”, Timestamp: time.Now()} jsonData, _ := json.Marshal(event) fmt.Println(string(jsonData)) // {“name”:”Party”,”timestamp”:”2023-10-01T10:00:00Z”}
  • 处理大型 JSON 数据或 IO 流(如 HTTP 请求体、文件)时,使用 json.Encoder 和 json.Decoder 可以提高内存效率。 流式编码(写入) file, err := os.Create(“output.json”) if err != nil { panic(err) } defer file.Close() encoder := json.NewEncoder(file) encoder.SetIndent(“”, ” “) // 设置缩进 err = encoder.Encode(data) // 将数据直接编码并写入文件 if err != nil { panic(err) } 流式解码(读取) file, err := os.Open(“large_data.json”) if err != nil { panic(err) } defer file.Close() decoder := json.NewDecoder(file) for { var item MyStruct if err := decoder.Decode(&item); err == io.EOF { break // 流结束 } else if err != nil { panic(err) } // 处理每一个 item fmt.Println(item) }
  • 通过实现 json.Marshaler 和 json.Unmarshaler 接口,你可以完全控制特定类型的编码和解码逻辑。 type Status int const ( Unknown Status = iota Active Inactive ) // 实现 MarshalJSON,将 Status 转换为字符串 func (s Status) MarshalJSON() ([]byte, error) { var str string switch s { case Active: str = “active” case Inactive: str = “inactive” default: str = “unknown” } return json.Marshal(str) } // 实现 UnmarshalJSON,将字符串转换回 Status func (s *Status) UnmarshalJSON(data []byte) error { var str string if err := json.Unmarshal(data, &str); err != nil { return err } switch str { case “active”: *s = Active case “inactive”: *s = Inactive default: *s = Unknown } return nil }
  • 避免使用 interface{}:尽可能使用明确类型的结构体,而不是 map[string]interface{},这能减少反射开销并提高类型安全。 重用 json.Decoder:在循环中解码多个 JSON 对象时,复用 json.Decoder 实例。 预分配切片:如果你知道解码后数组的大致大小,预分配切片容量可以减少扩容带来的开销。 **使用 json.RawMessage“ 延迟解析**:对于部分不需要立即处理的 JSON 数据,可以先存储为 json.RawMessage`,等到需要时再解析,从而减少不必要的解析开销。
  • Go 语言的 encoding/json 库提供了强大而灵活的 JSON 处理能力。掌握从基础编码解码到高级技巧,能让你更自如地应对各种数据处理场景。记住,明确的结构体定义优于 map[string]interface{},善用结构体标签能精细控制序列化行为,而 流式处理 则是处理大数据的利器。 到此这篇关于Go语言中json操作的实现的文章就介绍到这了,更多相关Go语言 json操作内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客! 您可能感兴趣的文章: Go语言中常用json处理操作小结 重学Go语言之JSON操作详解 Go语言中JSON文件的读写操作 go语言中json数据的读取和写出操作 Go语言设置JSON的默认值操作
  • 目录
    • 📊 一、JSON 与 Go 类型对应关系
    • 🛠️ 二、基本操作:编码与解码
    • 🔧 三、结构体标签(Struct Tags)的运用
    • 🧩 四、处理未知结构或动态 JSON
    • ⏰ 五、处理时间类型(time.Time)
    • 🚀 六、流式编码与解码
    • 🧠 七、自定义序列化与反序列化
    • ⚡ 八、性能优化建议
    • 💎 总结与建议

    理解 JSON 类型与 Go 类型的映射是处理 JSON 的基础:

    JSON 类型 Go 类型 说明
    对象 (object) struct, map[string]T 键值对集合
    数组 (array) []T (切片), [n]T (数组) 值的有序集合,通常使用切片更灵活
    字符串 (string) string
    数字 (number) int, float64 等 注意:JSON 中的数字默认解码为 float64
    布尔 (boolean) bool
    null nil

    Go 使用标准库 encoding/json 进行 JSON 处理。

    编码 (序列化,Marshal):使用 json.Marshal 将 Go 数据结构转换为 JSON 字节切片 ([]byte)。

    package main
    
    import (
        "encoding/json"
        "fmt"
    )
    
    type Person struct {
        Name string `json:"name"` // 通过标签指定JSON字段名
        Age  int    `json:"age"`
    }
    
    func main() {
        p := Person{Name: "Alice", Age: 30}
        jsonBytes, err := json.Marshal(p)
        if err != nil {
            panic(err)
        }
        fmt.Println(string(jsonBytes)) // 输出:{"name":"Alice","age":30}
    }
    

    解码 (反序列化,Unmarshal):使用 json.Unmarshal 将 JSON 字节切片解析回 Go 数据结构。

    jsonStr := `{"name":"Bob","age":25}`
    var p2 Person
    err = json.Unmarshal([]byte(jsonStr), &p2) // 注意传递指针
    if err != nil {
        panic(err)
    }
    fmt.Printf("%+vn", p2) // 输出:{Name:Bob Age:25}
    

    格式化输出:使用 json.MarshalIndent 生成带缩进的格式化 JSON,便于阅读。

    jsonBytes, err := json.MarshalIndent(p, "", "  ") // 前缀为空,缩进为两个空格
    if err != nil {
        panic(err)
    }
    fmt.Println(string(jsonBytes))
    /* 输出:
    {
      "name": "Alice",
      "age": 30
    }
    */
    

    结构体标签是控制 JSON 序列化和反序列化行为的关键。

    自定义字段名:使用 json:"field_name" 指定 JSON 字段名。

    type Product struct {
        ProductID int    `json:"id"`      // JSON中字段名为 "id"
        Name      string `json:"name"`    // JSON中字段名为 "name"
    }
    

    忽略字段

    type User struct {
        Name     string `json:"name"`
        Password string `json:"-"` // 序列化时忽略密码字段
    }
    
    type User struct {
        Name  string `json:"name"`
        Email string `json:"email,omitempty"` // 如果Email为空,则序列化时不包含此字段
        Age   int    `json:"age,omitempty"`   // 如果Age为0,则序列化时不包含此字段
    }
    
    • 永久忽略:使用 json:"-",该字段不会参与 JSON 序列化。
    • 条件忽略(空值):使用 omitempty,当字段为零值时(如空字符串、零值、nil指针等),序列化时省略该字段。

    字符串形式的数字:有时需要将数字类型以字符串形式序列化,或从字符串形式的数字反序列化,可以使用 ,string 标签选项。

    type MyData struct {
        Num int `json:"num,string"` // 序列化为 {"num": "123"}, 从 {"num": "123"} 反序列化
    }
    

    注意:此标签仅适用于 int, float, bool 等类型与字符串形式的互转,且 JSON 中对应的值必须是带引号的字符串。

    当你无法预知 JSON 的确切结构时,有两种主要方法:

    使用 map[string]interface{}:适合快速访问,但需要类型断言,安全性较低。

    jsonStr := `{"name":"Alice","age":30,"hobbies":["reading","traveling"]}`
    var data map[string]interface{}
    err := json.Unmarshal([]byte(jsonStr), &data)
    if err != nil {
        panic(err)
    }
    name := data["name"].(string) // 类型断言
    age := data["age"].(float64)   // JSON数字默认转为float64
    hobbies := data["hobbies"].([]interface{})
    fmt.Println(name, age, hobbies[0])
    

    使用 json.RawMessage:延迟解析,允许你先处理已知部分,再按需解析未知部分,更灵活安全。

    type Message struct {
        Name  string          `json:"name"`
        Extra json.RawMessage `json:"extra"` // 原始JSON字节,暂不解析
    }
    var msg Message
    json.Unmarshal([]byte(jsonStr), &msg)
    // 之后根据条件再解析 Extra 字段
    

    time.Time 类型默认序列化为 RFC 3339 格式的字符串。你也可以为 time.Time 定义自定义的 MarshalerUnmarshaler 接口实现,以适应特定的时间格式。

    type Event struct {
        Name      string    `json:"name"`
        Timestamp time.Time `json:"timestamp"` // 默认格式:2006-01-02T15:04:05Z07:00
    }
    event := Event{Name: "Party", Timestamp: time.Now()}
    jsonData, _ := json.Marshal(event)
    fmt.Println(string(jsonData)) // {"name":"Party","timestamp":"2023-10-01T10:00:00Z"}
    

    处理大型 JSON 数据或 IO 流(如 HTTP 请求体、文件)时,使用 json.Encoderjson.Decoder 可以提高内存效率。

    流式编码(写入)

    file, err := os.Create("output.json")
    if err != nil {
        panic(err)
    }
    defer file.Close()
    encoder := json.NewEncoder(file)
    encoder.SetIndent("", "  ") // 设置缩进
    err = encoder.Encode(data) // 将数据直接编码并写入文件
    if err != nil {
        panic(err)
    }
    

    流式解码(读取)

    file, err := os.Open("large_data.json")
    if err != nil {
        panic(err)
    }
    defer file.Close()
    decoder := json.NewDecoder(file)
    for {
        var item MyStruct
        if err := decoder.Decode(&item); err == io.EOF {
            break // 流结束
        } else if err != nil {
            panic(err)
        }
        // 处理每一个 item
        fmt.Println(item)
    }
    

    通过实现 json.Marshalerjson.Unmarshaler 接口,你可以完全控制特定类型的编码和解码逻辑。

    type Status int
    
    const (
        Unknown Status = iota
        Active
        Inactive
    )
    
    // 实现 MarshalJSON,将 Status 转换为字符串
    func (s Status) MarshalJSON() ([]byte, error) {
        var str string
        switch s {
        case Active:
            str = "active"
        case Inactive:
            str = "inactive"
        default:
            str = "unknown"
        }
        return json.Marshal(str)
    }
    
    // 实现 UnmarshalJSON,将字符串转换回 Status
    func (s *Status) UnmarshalJSON(data []byte) error {
        var str string
        if err := json.Unmarshal(data, &str); err != nil {
            return err
        }
        switch str {
        case "active":
            *s = Active
        case "inactive":
            *s = Inactive
        default:
            *s = Unknown
        }
        return nil
    }
    

    1. 避免使用 interface{}:尽可能使用明确类型的结构体,而不是 map[string]interface{},这能减少反射开销并提高类型安全。
    2. 重用 json.Decoder:在循环中解码多个 JSON 对象时,复用 json.Decoder 实例。
    3. 预分配切片:如果你知道解码后数组的大致大小,预分配切片容量可以减少扩容带来的开销。
    4. **使用 json.RawMessage“ 延迟解析**:对于部分不需要立即处理的 JSON 数据,可以先存储为 json.RawMessage`,等到需要时再解析,从而减少不必要的解析开销。

    Go 语言的 encoding/json 库提供了强大而灵活的 JSON 处理能力。掌握从基础编码解码到高级技巧,能让你更自如地应对各种数据处理场景。记住,明确的结构体定义优于 map[string]interface{},善用结构体标签能精细控制序列化行为,而 流式处理 则是处理大数据的利器。

    到此这篇关于Go语言中json操作的实现的文章就介绍到这了,更多相关Go语言 json操作内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!

    您可能感兴趣的文章:

    • Go语言中常用json处理操作小结
    • 重学Go语言之JSON操作详解
    • Go语言中JSON文件的读写操作
    • go语言中json数据的读取和写出操作
    • Go语言设置JSON的默认值操作

    站内搜索