JSON处理 #

JSON(JavaScript Object Notation)是现代应用中最常用的数据交换格式之一。Go语言通过 encoding/json 包提供了强大的JSON处理能力。

基本编码 #

结构体转JSON #

使用 json.Marshal 将Go值转换为JSON:

go
package main

import (
    "encoding/json"
    "fmt"
)

type Person struct {
    Name string
    Age  int
    City string
}

func main() {
    person := Person{
        Name: "张三",
        Age:  30,
        City: "北京",
    }
    
    data, err := json.Marshal(person)
    if err != nil {
        fmt.Println("编码失败:", err)
        return
    }
    
    fmt.Println(string(data))
}

格式化输出 #

使用 json.MarshalIndent 生成格式化的JSON:

go
package main

import (
    "encoding/json"
    "fmt"
)

type Person struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
    City string `json:"city"`
}

func main() {
    person := Person{
        Name: "张三",
        Age:  30,
        City: "北京",
    }
    
    data, _ := json.MarshalIndent(person, "", "  ")
    fmt.Println(string(data))
}

输出:

json
{
  "name": "张三",
  "age": 30,
  "city": "北京"
}

结构体标签 #

使用结构体标签控制JSON字段映射:

go
package main

import (
    "encoding/json"
    "fmt"
)

type User struct {
    ID       int    `json:"id"`
    Username string `json:"username"`
    Email    string `json:"email,omitempty"`
    Password string `json:"-"`
    IsActive bool   `json:"is_active"`
}

func main() {
    user := User{
        ID:       1,
        Username: "admin",
        Password: "secret123",
        IsActive: true,
    }
    
    data, _ := json.MarshalIndent(user, "", "  ")
    fmt.Println(string(data))
}

常用标签:

标签 说明
json:"name" 指定JSON字段名
json:"name,omitempty" 字段为零值时忽略
json:"-" 完全忽略该字段
json:",string" 将数值作为字符串输出

基本解码 #

JSON转结构体 #

使用 json.Unmarshal 将JSON转换为Go值:

go
package main

import (
    "encoding/json"
    "fmt"
)

type Person struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
    City string `json:"city"`
}

func main() {
    jsonStr := `{"name":"李四","age":25,"city":"上海"}`
    
    var person Person
    err := json.Unmarshal([]byte(jsonStr), &person)
    if err != nil {
        fmt.Println("解码失败:", err)
        return
    }
    
    fmt.Printf("%+v\n", person)
}

JSON转map #

当JSON结构不确定时,可以解码到 map[string]interface{}

go
package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    jsonStr := `{
        "name": "王五",
        "age": 28,
        "hobbies": ["读书", "运动"],
        "address": {
            "city": "广州",
            "street": "中山路"
        }
    }`
    
    var data map[string]interface{}
    json.Unmarshal([]byte(jsonStr), &data)
    
    fmt.Println("姓名:", data["name"])
    fmt.Println("年龄:", data["age"])
    
    if address, ok := data["address"].(map[string]interface{}); ok {
        fmt.Println("城市:", address["city"])
    }
}

处理数组 #

JSON数组 #

go
package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    jsonStr := `["苹果", "香蕉", "橙子"]`
    
    var fruits []string
    json.Unmarshal([]byte(jsonStr), &fruits)
    
    for i, fruit := range fruits {
        fmt.Printf("%d: %s\n", i+1, fruit)
    }
}

对象数组 #

go
package main

import (
    "encoding/json"
    "fmt"
)

type Product struct {
    Name  string  `json:"name"`
    Price float64 `json:"price"`
}

func main() {
    jsonStr := `[
        {"name": "笔记本", "price": 5999.00},
        {"name": "鼠标", "price": 99.00},
        {"name": "键盘", "price": 299.00}
    ]`
    
    var products []Product
    json.Unmarshal([]byte(jsonStr), &products)
    
    var total float64
    for _, p := range products {
        fmt.Printf("%s: ¥%.2f\n", p.Name, p.Price)
        total += p.Price
    }
    fmt.Printf("总计: ¥%.2f\n", total)
}

流式处理 #

编码到文件 #

go
package main

import (
    "encoding/json"
    "os"
)

type Config struct {
    AppName string `json:"app_name"`
    Version string `json:"version"`
    Debug   bool   `json:"debug"`
}

func main() {
    config := Config{
        AppName: "MyApp",
        Version: "1.0.0",
        Debug:   true,
    }
    
    file, _ := os.Create("config.json")
    defer file.Close()
    
    encoder := json.NewEncoder(file)
    encoder.SetIndent("", "  ")
    encoder.Encode(config)
}

从文件解码 #

go
package main

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

type Config struct {
    AppName string `json:"app_name"`
    Version string `json:"version"`
    Debug   bool   `json:"debug"`
}

func main() {
    file, err := os.Open("config.json")
    if err != nil {
        fmt.Println("打开文件失败:", err)
        return
    }
    defer file.Close()
    
    var config Config
    decoder := json.NewDecoder(file)
    
    if err := decoder.Decode(&config); err != nil {
        fmt.Println("解码失败:", err)
        return
    }
    
    fmt.Printf("%+v\n", config)
}

自定义编码 #

实现 MarshalJSON #

go
package main

import (
    "encoding/json"
    "fmt"
    "strings"
    "time"
)

type Event struct {
    Name      string
    Timestamp time.Time
}

func (e Event) MarshalJSON() ([]byte, error) {
    type Alias Event
    return json.Marshal(&struct {
        Timestamp string `json:"timestamp"`
        *Alias
    }{
        Timestamp: e.Timestamp.Format("2006-01-02 15:04:05"),
        Alias:     (*Alias)(&e),
    })
}

func main() {
    event := Event{
        Name:      "系统启动",
        Timestamp: time.Now(),
    }
    
    data, _ := json.MarshalIndent(event, "", "  ")
    fmt.Println(string(data))
}

实现 UnmarshalJSON #

go
package main

import (
    "encoding/json"
    "fmt"
    "strings"
    "time"
)

type FlexibleTime struct {
    time.Time
}

func (ft *FlexibleTime) UnmarshalJSON(data []byte) error {
    str := strings.Trim(string(data), `"`)
    
    layouts := []string{
        "2006-01-02",
        "2006-01-02 15:04:05",
        time.RFC3339,
    }
    
    var err error
    for _, layout := range layouts {
        ft.Time, err = time.Parse(layout, str)
        if err == nil {
            return nil
        }
    }
    
    return err
}

func main() {
    jsonStr := `"2024-01-15 14:30:00"`
    
    var ft FlexibleTime
    json.Unmarshal([]byte(jsonStr), &ft)
    
    fmt.Println("解析时间:", ft.Format("2006年01月02日 15:04"))
}

处理未知结构 #

使用 RawMessage #

go
package main

import (
    "encoding/json"
    "fmt"
)

type Message struct {
    Type string          `json:"type"`
    Data json.RawMessage `json:"data"`
}

type TextData struct {
    Content string `json:"content"`
}

type ImageData struct {
    URL    string `json:"url"`
    Width  int    `json:"width"`
    Height int    `json:"height"`
}

func main() {
    jsonStr := `{
        "type": "text",
        "data": {"content": "Hello, World!"}
    }`
    
    var msg Message
    json.Unmarshal([]byte(jsonStr), &msg)
    
    switch msg.Type {
    case "text":
        var text TextData
        json.Unmarshal(msg.Data, &text)
        fmt.Println("文本:", text.Content)
    case "image":
        var image ImageData
        json.Unmarshal(msg.Data, &image)
        fmt.Printf("图片: %s (%dx%d)\n", image.URL, image.Width, image.Height)
    }
}

实用示例:配置文件处理 #

go
package main

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

type DatabaseConfig struct {
    Host     string `json:"host"`
    Port     int    `json:"port"`
    Username string `json:"username"`
    Password string `json:"password"`
    Database string `json:"database"`
}

type ServerConfig struct {
    Port int    `json:"port"`
    Host string `json:"host"`
}

type AppConfig struct {
    Server   ServerConfig   `json:"server"`
    Database DatabaseConfig `json:"database"`
    Debug    bool           `json:"debug"`
}

func LoadConfig(filename string) (*AppConfig, error) {
    file, err := os.Open(filename)
    if err != nil {
        return nil, err
    }
    defer file.Close()
    
    var config AppConfig
    decoder := json.NewDecoder(file)
    
    if err := decoder.Decode(&config); err != nil {
        return nil, err
    }
    
    return &config, nil
}

func SaveConfig(filename string, config *AppConfig) error {
    file, err := os.Create(filename)
    if err != nil {
        return err
    }
    defer file.Close()
    
    encoder := json.NewEncoder(file)
    encoder.SetIndent("", "  ")
    
    return encoder.Encode(config)
}

func main() {
    config := &AppConfig{
        Server: ServerConfig{
            Port: 8080,
            Host: "localhost",
        },
        Database: DatabaseConfig{
            Host:     "localhost",
            Port:     3306,
            Username: "root",
            Password: "password",
            Database: "myapp",
        },
        Debug: true,
    }
    
    SaveConfig("app.json", config)
    fmt.Println("配置已保存")
    
    loaded, _ := LoadConfig("app.json")
    fmt.Printf("服务器: %s:%d\n", loaded.Server.Host, loaded.Server.Port)
    fmt.Printf("数据库: %s@%s:%d/%s\n",
        loaded.Database.Username,
        loaded.Database.Host,
        loaded.Database.Port,
        loaded.Database.Database,
    )
}

小结 #

函数/类型 用途
json.Marshal Go值转JSON字节切片
json.Unmarshal JSON字节切片转Go值
json.MarshalIndent 格式化JSON输出
json.Encoder 流式JSON编码
json.Decoder 流式JSON解码
json.RawMessage 延迟解码JSON片段
struct tags 控制字段映射和选项

JSON处理是现代应用开发的核心技能,掌握这些技术可以轻松处理API响应、配置文件和数据存储等场景。