ZhouChuang's Blog ZhouChuang's Blog
首页
  • 常用命令
  • 日常总结
  • Prometheus
  • 基础
  • 常用库
专题
  • 「编码」
更多
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Zhou Chuang

为学日益 为道日损
首页
  • 常用命令
  • 日常总结
  • Prometheus
  • 基础
  • 常用库
专题
  • 「编码」
更多
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Go语言基础

  • 常用库

  • 数据库相关内容

  • 技巧

    • Go 处理 json
      • Json
        • 特点
        • 基本结构
        • 优缺点
      • 基本的序列化和反序列化
        • 基本概念
        • 常用函数
        • 简单示例
        • 序列化:
        • 反序列化:
        • 结构体 tag 介绍
        • 流式处理 JSON
      • 第三方json库
        • Sonic
        • Sonic的特点
        • 对比标准库
        • 注意事项
        • gjson
        • gjson 的特点
        • 基本用法
        • 小结
      • refer
    • Go 执行定时任务
  • golang
  • 技巧
ZhouChuang
2025-03-18
目录

Go 处理 json

# Json

JSON(JavaScript Object Notation,JavaScript对象表示法)是一种轻量级的数据交换格式,易于人和机器读取和编写。它最初源于JavaScript,但现在已经成为一种独立于语言的格式,被广泛应用于各种编程语言和系统中。

# 特点

简单易读:JSON使用键值对的形式,语法简洁,类似于许多编程语言中的对象或字典。

轻量:相比XML等格式,JSON更紧凑,数据传输效率更高。

跨语言支持:JSON是文本格式,几乎所有编程语言(如Python、Java、C++等)都有解析和生成JSON的工具。

用途广泛:常用于Web开发中的数据传输(如API响应)、配置文件存储等。

# 基本结构

JSON主要由以下几种数据类型组成:

  1. 对象(Object):

    • 用大括号 {} 包裹,包含键值对。

    • 键是字符串,值可以是字符串、数字、布尔值、数组、对象或null。

    • 示例:

      {
        "name": "Alice",
        "age": 25,
        "isStudent": false
      }
      
      1
      2
      3
      4
      5
  2. 数组(Array):

    • 用方括号 [] 包裹,可以包含多个值。

    • 示例:

      ["apple", "banana", "orange"]
      
      1
  3. 基本数据类型:

    • 字符串(String):用双引号包裹,例如 "hello"。

    • 数字(Number):可以是整数或浮点数,例如 42 或 3.14。

    • 布尔值(Boolean):true 或 false。

    • 空值(Null):null,表示无值。

示例: 一个更复杂一些的JSON示例:

{
  "person": {
    "name": "Bob",
    "age": 30,
    "hobbies": ["reading", "gaming"],
    "address": {
      "street": "123 Main St",
      "city": "Shanghai"
    },
    "active": true,
    "score": null
  }
1
2
3
4
5
6
7
8
9
10
11
12

# 优缺点

优点:

  • 易于解析和生成。
  • 结构化数据表达能力强。
  • 广泛支持,适合跨平台使用。

缺点:

  • 不支持注释(官方标准中),不便于添加说明。
  • 数据类型较少(例如没有日期类型)。

# 基本的序列化和反序列化

在Go语言(Golang)中,操作JSON非常简单且高效,标准库 encoding/json 提供了强大的工具来编码(marshal)和解码(unmarshal)JSON数据。

# 基本概念

  • Marshal:将Go数据结构(如结构体、切片、映射等)转换为JSON字符串。
  • Unmarshal:将JSON字符串解析为Go数据结构。
  • Go通过结构体标签(struct tags)来控制JSON字段的名称和行为。

# 常用函数

  • json.Marshal(v interface{}) ([]byte, error):将数据编码为JSON字节切片。
  • json.Unmarshal(data []byte, v interface{}) error:将JSON字节切片解码到Go变量中。
  • json.NewEncoder(w io.Writer) 和 json.NewDecoder(r io.Reader):用于流式编码和解码(如文件或网络数据)。

# 简单示例

# 序列化:

func Marshal(v interface{}) ([]byte, error)
1
package main

import (
	"encoding/json"
	"fmt"
)

type Person struct {
	Name    string `json:"name"`         // 字段名在JSON中为 "name"
	Age     int    `json:"age"`          // 字段名在JSON中为 "age"
	Email   string `json:"email,omitempty"` // omitempty 表示如果为空则忽略该字段
	Private string `json:"-"`            // "-" 表示忽略该字段
}

func main() {
	p := Person{
		Name:    "Alice",
		Age:     25,
		Email:   "",       // 空值,输出时忽略
		Private: "secret", // 被忽略
	}

	// 转换为JSON
	data, err := json.Marshal(p)
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	fmt.Println(string(data)) // 输出: {"name":"Alice","age":25}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# 反序列化:

func Unmarshal(data []byte, v interface{}) error
1
package main

import (
	"encoding/json"
	"fmt"
)

type Person struct {
	Name  string `json:"name"`
	Age   int    `json:"age"`
	Email string `json:"email,omitempty"`
}

func main() {
	// JSON字符串
	jsonStr := `{"name":"Bob","age":30,"email":"bob@example.com"}`

	// 定义接收数据的变量
	var p Person

	// 解析JSON
	err := json.Unmarshal([]byte(jsonStr), &p)
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	fmt.Printf("Name: %s, Age: %d, Email: %s\n", p.Name, p.Age, p.Email)
	// 输出: Name: Bob, Age: 30, Email: bob@example.com
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# 结构体 tag 介绍

Tag是结构体的元信息,可以在运行的时候通过反射的机制读取出来。 Tag在结构体字段的后方定义,由一对反引号包裹起来,具体的格式如下:

`key1:"value1" key2:"value2"`
1

结构体tag由一个或多个键值对组成。键与值使用冒号分隔,值用双引号括起来。同一个结构体字段可以设置多个键值对tag,不同的键值对之间使用空格分隔。

# 流式处理 JSON

对于大文件或网络传输,可以使用 json.Encoder 和 json.Decoder。

序列化:

package main

import (
	"encoding/json"
	"os"
)

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

func main() {
	p := Person{Name: "David", Age: 40}

	file, _ := os.Create("person.json")
	defer file.Close()

	encoder := json.NewEncoder(file)
	encoder.Encode(p) // 写入JSON到文件
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

反序列化:

package main

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

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

func main() {
	file, _ := os.Open("person.json")
	defer file.Close()

	var p Person
	decoder := json.NewDecoder(file)
	decoder.Decode(&p)

	fmt.Printf("Name: %s, Age: %d\n", p.Name, p.Age)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 第三方json库

# Sonic

Sonic(github.com/bytedance/sonic)是一个由字节跳动(ByteDance)开发的高性能JSON序列化和反序列化库。它通过使用JIT(即时编译)和SIMD(单指令多数据)技术显著提升了JSON处理的效率,特别适合需要处理大量JSON数据的场景,比如微服务或高吞吐量的API。

# Sonic的特点

  • 高性能:相比标准库 encoding/json,Sonic在编码和解码速度上快得多,尤其是在处理大JSON数据时。
  • 兼容性:基本兼容标准库的API,可以作为drop-in replacement(直接替换),只需更改导入路径。
  • 技术亮点
    • JIT:动态生成针对特定数据结构的优化代码,减少反射和函数调用开销。
    • SIMD:利用CPU的并行指令集加速字符串处理和数值转换。
  • 适用场景:Sonic在各种JSON大小(小到几字节,大到几MB)和使用场景(通用解析、绑定解析、并行处理)中都表现出色。
package main

import (
    "fmt"
    "github.com/bytedance/sonic"
)

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

func main() {
    p := Person{Name: "Alice", Age: 25}

    // 编码
    data, err := sonic.Marshal(p)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    fmt.Println(string(data)) // {"name":"Alice","age":25}

    // 解码
    var p2 Person
    err = sonic.Unmarshal(data, &p2)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    fmt.Println(p2.Name, p2.Age) // Alice 25
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# 对比标准库

goos: linux
goarch: amd64
pkg: test-Sonic
cpu: AMD Ryzen 7 PRO 6850H with Radeon Graphics
BenchmarkJSONMarshal-16          1000000              1019 ns/op             496 B/op          9 allocs/op
BenchmarkSonicMarshal-16         2376646               489.0 ns/op           394 B/op          4 allocs/op
BenchmarkJSONUnmarshal-16         460765              2526 ns/op             424 B/op         22 allocs/op
BenchmarkSonicUnmarshal-16       2747349               437.9 ns/op           229 B/op          2 allocs/op
PASS
ok      test-Sonic        5.558s
1
2
3
4
5
6
7
8
9
10

# 注意事项

  • 环境支持:Sonic依赖特定的CPU架构(如x86_64)和操作系统(Linux、macOS、Windows),在不支持的环境中会回退到标准库 encoding/json。
  • 内存使用:Sonic在解析时可能引用原始JSON缓冲区以提升性能,这可能增加内存占用(通常是解码对象的20%-80%)。
  • 预编译(Pretouch):对于大结构体,建议使用 sonic.Pretouch 预编译以避免首次运行时的JIT延迟。

# gjson

gjson 是一个轻量级、高性能的Go语言JSON解析库,由 github.com/tidwall/gjson 提供。它专注于快速读取JSON数据,而不需要将整个JSON反序列化到结构体中。相比标准库 encoding/json,gjson 的设计目标是简单、快速和灵活,特别适合只需要从JSON中提取部分数据而非完整解析的场景。

# gjson 的特点

  1. 无需结构体:
    • 不需要预定义Go结构体,直接通过路径(path)访问JSON中的字段。
    • 适合处理动态或未知结构的JSON。
  2. 高性能:
    • 通过避免反射和完整反序列化,gjson 的解析速度通常比标准库快得多。
    • 使用零拷贝(zero-copy)技术,直接操作原始JSON字节切片,减少内存分配。
  3. 简单易用:
    • 提供直观的路径查询语法,类似于JavaScript中的对象访问。
    • 支持嵌套字段、数组索引和通配符。
  4. 只读:
    • gjson 专注于读取JSON,不支持序列化(Marshal)或修改JSON(如果需要修改,可以搭配 sjson 使用)。
  5. 轻量:
    • 代码库小巧,依赖少,易于集成。

# 基本用法

gjson 的核心函数是 Get,它接受JSON字符串(或字节切片)和路径,返回一个 Result 类型的值。

示例1:简单字段访问:

package main

import (
    "fmt"
    "github.com/tidwall/gjson"
)

func main() {
    jsonStr := `{"name": "Alice", "age": 25, "city": "Shanghai"}`

    // 获取字段
    name := gjson.Get(jsonStr, "name").String()
    age := gjson.Get(jsonStr, "age").Int()

    fmt.Println("Name:", name) // Name: Alice
    fmt.Println("Age:", age)   // Age: 25
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

示例2:嵌套字段和数组

func main() {
    jsonStr := `{
        "person": {
            "name": "Bob",
            "hobbies": ["reading", "gaming"]
        }
    }`

    // 嵌套字段
    name := gjson.Get(jsonStr, "person.name").String()
    // 数组索引
    hobby1 := gjson.Get(jsonStr, "person.hobbies.0").String()

    fmt.Println("Name:", name)     // Name: Bob
    fmt.Println("Hobby 1:", hobby1) // Hobby 1: reading
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

路径语法

gjson 使用点号(.)和数组索引访问JSON字段,支持以下特性:

  • 基本字段:field(如 "name")。
  • 嵌套字段:parent.child(如 "person.name")。
  • 数组索引:array.index(如 "hobbies.0")。
  • 通配符:
    • #:表示数组长度或匹配所有元素。
    • *:匹配所有字段。
  • 修饰符:在路径后添加 | 和修饰符(如 #(condition))进行高级查询。

示例3:通配符和修饰符

func main() {
    jsonStr := `[
        {"id": 1, "name": "Alice"},
        {"id": 2, "name": "Bob"}
    ]`

    // 获取数组长度
    count := gjson.Get(jsonStr, "#").Int()
    fmt.Println("Count:", count) // Count: 2

    // 获取所有name字段
    names := gjson.Get(jsonStr, "*.name")
    names.ForEach(func(key, value gjson.Result) bool {
        fmt.Println("Name:", value.String()) // Name: Alice, Name: Bob
        return true
    })

    // 查找id为2的name
    name := gjson.Get(jsonStr, "#(id==2)#.name").String()
    fmt.Println("Name with id 2:", name) // Name with id 2: Bob
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

返回值类型(Result)

gjson.Get 返回一个 Result 结构体,支持多种类型转换:

  • .String():转换为字符串。
  • .Int():转换为int64。
  • .Float():转换为float64。
  • .Bool():转换为布尔值。
  • .Array():转换为 []gjson.Result。
  • .Map():转换为 map[string]gjson.Result。
  • .Raw:获取原始JSON字符串。

如果路径不存在,Result.Exists() 返回 false,可以用来检查有效性。

jsonStr := `{"name": "Charlie"}`
result := gjson.Get(jsonStr, "age")
if !result.Exists() {
    fmt.Println("Age does not exist") // Age does not exist
}
1
2
3
4
5

# 小结

在单键查找场景中, gjson (opens new window)具有巨大的优势。这是因为它的查找是通过惰性加载机制实现的,巧妙地跳过了传递的值,并有效的减少了许多不必要的解析。实际应用证明,在产品中充分利用这个特性确实能带来收益。但是,当涉及到多键查找时,Gjson甚至比标准库还要差,这是其跳过机制的副作用——搜索相同路径会导致重复解析(跳过解析也是一种轻量的解析)因此,根据实际情况准确的做出调整是关键问题。

多键查找:

BenchmarkJSONMarshal-16          1280576               941.7 ns/op           496 B/op          9 allocs/op
BenchmarkSonicMarshal-16         2474157               488.0 ns/op           394 B/op          4 allocs/op
BenchmarkJSONUnmarshal-16         466876              2476 ns/op             424 B/op         22 allocs/op
BenchmarkSonicUnmarshal-16       2798126               427.5 ns/op           228 B/op          2 allocs/op
BenchmarkGJSONUnmarshal-16       1263700               956.3 ns/op          1368 B/op          8 allocs/op
1
2
3
4
5

单键查找:

BenchmarkJSONMarshal-16          1273489               941.5 ns/op           496 B/op          9 allocs/op
BenchmarkSonicMarshal-16         2430908               491.3 ns/op           394 B/op          4 allocs/op
BenchmarkJSONUnmarshal-16         480385              2474 ns/op             424 B/op         22 allocs/op
BenchmarkSonicUnmarshal-16       2791281               429.5 ns/op           228 B/op          2 allocs/op
BenchmarkGJSONUnmarshal-16      15421743                76.26 ns/op            8 B/op          1 allocs/op
1
2
3
4
5

# refer

https://www.json.org/json-en.html (opens new window)

#json#gjson#Sonic
上次更新: 2025/05/06, 13:52:56
sqlx 库
Go 执行定时任务

← sqlx 库 Go 执行定时任务→

最近更新
01
find 命令介绍
05-06
02
交换分区的配置
04-09
03
top 命令介绍
04-08
更多文章>
Theme by Vdoing | Copyright © 2019-2025 Zhou Chuang 版权所有| 鲁ICP备2021031629号-2
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式