开源项目|2000行代码的 `JSON` 库

193 阅读3分钟

虽然一直在开发中使用 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 - 使用自定义配置解析 JSON
  • UnmarshalFromReader(r io.Reader, v interface{}) error - 从 Reader 解析 JSON
  • UnmarshalFromReaderWithConfig(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 库采用了多种性能优化技术:

  1. 直接解码:无需中间 Value 对象,直接解码到目标 Go 对象
  2. 对象池:使用 sync.Pool 减少内存分配
  3. 预分配内存:为数组和切片预分配适当容量
  4. 类型特化:针对常见类型提供专用编码/解码路径
  5. 常量缓存:预生成常用数字和字符串常量
  6. 减少反射:尽可能减少反射操作,提高性能