虽然一直在开发中使用 JSON 解析,但是如何高效的从 0 实现一个 JSON 解析,一直还未尝试,花了几个周末的时间用 golang 实现 sjson。
github:github.com/linkxzhou/s…
功能
sjson 是一个高性能的 Go 语言 JSON 解析库,提供了高效的 JSON 编码和解码功能。它采用直接解码技术,无需中间 Value 对象,从而提高解析效率。
架构
特性
- 简单易用的 API,与标准库
encoding/json接口兼容 - 高性能直接解码器实现,无需中间 Value 对象
- 支持基本的 JSON 数据类型:null、布尔值、数字、字符串、数组和对象
- 支持结构体与 JSON 的相互转换,支持
json标签 - 提供流式解析功能,可从字符串或 Reader 中解析 JSON
- 使用对象池和内存复用技术,减少内存分配和 GC 压力
- 针对常见类型和场景进行了性能优化
- 代码精简,主要逻辑代码 2000 行
安装
go get github.com/linkxzhou/sjson
性能对比
sjson 库的性能目标是接近或超过最新 v2 标准库 encoding/json,同时提供更简洁的 API 和更好的可扩展性。
1. Unmarshal 性能对比图(ns/op)
2. Unmarshal 吞吐量对比图(MB/s)
3. Marshal 性能对比图(ns/op)
4. Marshal 吞吐量对比图(MB/s)
运行测试
# 运行所有测试
go test -v ./...
# 运行测试并生成覆盖率报告
go test -v -race -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
# 运行基准测试
go test -bench=. -benchmem ./...
# 运行代码质量检查
golangci-lint run
使用示例
解析 JSON 字符串
package main
import (
"fmt"
"github.com/linkxzhou/sjson"
)
func main() {
// 解析 JSON 到 interface{}
var data interface{}
jsonStr := `{"name":"张三","age":30,"skills":["Go","Python"]}`
err := sjson.Unmarshal([]byte(jsonStr), &data)
if err != nil {
fmt.Println("解析错误:", err)
return
}
fmt.Printf("%+v\n", data)
// 解析 JSON 到结构体
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Skills []string`json:"skills"`
}
var person Person
err = sjson.Unmarshal([]byte(jsonStr), &person)
if err != nil {
fmt.Println("解析错误:", err)
return
}
fmt.Printf("%+v\n", person)
}
生成 JSON 字符串
package main
import (
"fmt"
"github.com/linkxzhou/sjson"
)
func main() {
// 从结构体生成 JSON
person := struct {
Name string `json:"name"`
Age int `json:"age"`
Skills []string`json:"skills"`
}{
Name: "李四",
Age: 25,
Skills: []string{"Java", "C++"},
}
data, err := sjson.Marshal(person)
if err != nil {
fmt.Println("编码错误:", err)
return
}
fmt.Println(string(data))
}
从 Reader 解析 JSON
package main
import (
"fmt"
"github.com/linkxzhou/sjson"
"strings"
)
func main() {
// 从 Reader 解析 JSON
jsonReader := strings.NewReader(`{"success":true,"data":{"items":[1,2,3]}}`)
var result struct {
Success bool`json:"success"`
Data struct {
Items []int`json:"items"`
} `json:"data"`
}
err := sjson.UnmarshalFromReader(jsonReader, &result)
if err != nil {
fmt.Println("解析错误:", err)
return
}
fmt.Printf("success: %v, items: %v\n", result.Success, result.Data.Items)
}
自定义配置
package main
import (
"fmt"
"github.com/linkxzhou/sjson"
)
func main() {
// 使用自定义配置
config := sjson.Config{
SortMapKeys: true, // 对 map 的键进行排序
}
data := map[string]interface{}{
"z": 1,
"a": 2,
"m": 3,
}
// 使用自定义配置进行编码
jsonBytes, _ := sjson.MarshalWithConfig(data, config)
fmt.Println(string(jsonBytes)) // 输出键已排序的 JSON
}
API 文档
解码函数
Unmarshal(data []byte, v interface{}) error- 将 JSON 字节切片解析为 Go 对象UnmarshalWithConfig(data []byte, v interface{}, config Config) error- 使用自定义配置解析 JSONUnmarshalFromReader(r io.Reader, v interface{}) error- 从 Reader 解析 JSONUnmarshalFromReaderWithConfig(r io.Reader, v interface{}, config Config) error- 使用自定义配置从 Reader 解析 JSON
编码函数
Marshal(v interface{}) ([]byte, error)- 将 Go 对象编码为 JSON 字节切片MarshalString(v interface{}) (string, error)- 将 Go 对象编码为 JSON 字符串MarshalWithConfig(v interface{}, config Config) ([]byte, error)- 使用自定义配置编码 JSON
配置选项
Config- 用于配置 JSON 解析和编码的行为-
SortMapKeys- 控制对象和 map 的键是否排序,默认不排序
性能优化
sjson 库采用了多种性能优化技术:
- 直接解码:无需中间 Value 对象,直接解码到目标 Go 对象
- 对象池:使用 sync.Pool 减少内存分配
- 预分配内存:为数组和切片预分配适当容量
- 类型特化:针对常见类型提供专用编码/解码路径
- 常量缓存:预生成常用数字和字符串常量
- 减少反射:尽可能减少反射操作,提高性能