专注于 Golang 相关文章和资料的开源项目 [go-home] ,欢迎关注!

Go语言的encoding/json包是处理JSON数据的标准库,除了基础的Marshal和Unmarshal功能外,还提供了许多实用的特性。本文整理了10个常用技巧,帮助开发者更高效地处理JSON数据。


1. 基础序列化 - 结构体与JSON互转

最常用的JSON操作,但很多人不知道的细节:

type User struct {
    Name  string `json:"name"`           // 字段映射
    Email string `json:"email,omitempty"` // 空值忽略
    Age   int    `json:"-"`              // 忽略字段
}

// 序列化
user := User{Name: "张三", Email: "test@example.com"}
data, _ := json.Marshal(user)

// 反序列化
var newUser User
json.Unmarshal(data, &newUser)

2. 动态JSON处理 - 不定结构也能搞定

遇到不确定的JSON结构?用interface{}和类型断言:

// 处理任意JSON
var result map[string]interface{}
json.Unmarshal(jsonData, &result)

// 类型断言获取值
if name, ok := result["name"].(string); ok {
    fmt.Println("Name:", name)
}

// 嵌套结构
if items, ok := result["items"].([]interface{}); ok {
    for _, item := range items {
        // 处理每个item
    }
}

3. 自定义序列化 - 控制输出格式

需要特殊的JSON格式?实现Marshaler接口:

type Timestamp time.Time

func (t Timestamp) MarshalJSON() ([]byte, error) {
    stamp := fmt.Sprintf("\"%s\"", time.Time(t).Format("2006-01-02 15:04:05"))
    return []byte(stamp), nil
}

func (t *Timestamp) UnmarshalJSON(data []byte) error {
    str := string(data)
    str = str[1 : len(str)-1] // 去除引号
    parsed, err := time.Parse("2006-01-02 15:04:05", str)
    *t = Timestamp(parsed)
    return err
}

4. 流式处理 - 大文件不怕内存爆

处理大型JSON文件,一次性加载会爆内存?用Decoder流式处理:

// 读取大文件
file, _ := os.Open("large.json")
decoder := json.NewDecoder(file)

// 逐个解析
for decoder.More() {
    var item DataItem
    if err := decoder.Decode(&item); err != nil {
        break
    }
    // 处理单个item,不会全部加载到内存
    processItem(item)
}

5. JSON Number - 精度不丢失

处理大数字或需要精确控制数值类型?用json.Number:

type Config struct {
    ID    json.Number `json:"id"`
    Price json.Number `json:"price"`
}

// 使用
var config Config
decoder := json.NewDecoder(strings.NewReader(jsonStr))
decoder.UseNumber() // 启用Number类型
decoder.Decode(&config)

// 按需转换
id, _ := config.ID.Int64()
price, _ := config.Price.Float64()

6. 嵌入式结构 - 代码复用神器

避免重复定义相同字段,用嵌入式结构:

type BaseResponse struct {
    Code    int    `json:"code"`
    Message string `json:"message"`
}

type UserResponse struct {
    BaseResponse // 嵌入
    Data User    `json:"data"`
}

// JSON输出会平铺所有字段
// {"code":200,"message":"success","data":{...}}

7. RawMessage - 延迟解析

不确定某个字段的具体类型?先存起来后面再解析:

type Message struct {
    Type string          `json:"type"`
    Data json.RawMessage `json:"data"` // 原始JSON
}

// 根据type决定如何解析data
var msg Message
json.Unmarshal(jsonData, &msg)

switch msg.Type {
case "user":
    var user User
    json.Unmarshal(msg.Data, &user)
case "order":
    var order Order
    json.Unmarshal(msg.Data, &order)
}

8. 美化输出 - 调试必备

需要人类可读的JSON输出?用MarshalIndent:

// 普通输出
data, _ := json.Marshal(user)
// {"name":"张三","age":25}

// 美化输出
prettyData, _ := json.MarshalIndent(user, "", "  ")
// {
//   "name": "张三",
//   "age": 25
// }

9. 验证JSON - 避免panic

处理不可信的JSON数据?先验证再解析:

// 检查JSON是否有效
if json.Valid(jsonData) {
    var result Data
    json.Unmarshal(jsonData, &result)
} else {
    // 处理无效JSON
    return errors.New("invalid JSON")
}

// 严格模式解析
decoder := json.NewDecoder(reader)
decoder.DisallowUnknownFields() // 遇到未知字段报错
err := decoder.Decode(&result)

10. 性能优化 - 提速技巧

处理大量JSON时的性能优化技巧:

// 复用Encoder/Decoder
var bufferPool = sync.Pool{
    New: func() interface{} {
        return new(bytes.Buffer)
    },
}

func FastMarshal(v interface{}) []byte {
    buf := bufferPool.Get().(*bytes.Buffer)
    defer bufferPool.Put(buf)
    buf.Reset()
    
    encoder := json.NewEncoder(buf)
    encoder.Encode(v)
    return buf.Bytes()
}

// 预分配切片容量
var items []Item
json.Unmarshal(data, &items)
// 如果知道大概数量,预分配更高效
items = make([]Item, 0, 1000)

总结

以上介绍了encoding/json包的10个实用技巧,涵盖了从基础使用到高级特性的各个方面。合理运用这些功能可以让JSON处理代码更加简洁、高效和健壮。在实际开发中,根据具体场景选择合适的方法,能够有效提升开发效率和代码质量。