Golang高性能JSON编解码器GoJay - 目前速度最快的实现

Golang高性能JSON编解码器GoJay - 目前速度最快的实现

francoispqt/gojay

头像

gojay - 为Golang打造的高性能JSON编码器/解码器,配备强大的流式API


刚刚发布了高性能JSON编码器/解码器的0.9.0版本(目前速度最快)。

它提供简洁的API,通过简单接口实现对结构体、数组、切片甚至通道的编解码。同时还具备强大的流式解码功能。

与大多数编解码器采用的方法不同,我们非常欢迎来自社区的反馈意见。

诚挚欢迎反馈、代码审查和贡献。


更多关于Golang高性能JSON编解码器GoJay - 目前速度最快的实现的实战教程也可以访问 https://www.itying.com/category-94-b0.html

6 回复

这看起来真的非常有趣!

你进行这个项目的动机是什么?背后的设计选择有哪些?我研究过不少 Go 语言的 JSON 解析器,但之前从未见过类似这样的实现。

更多关于Golang高性能JSON编解码器GoJay - 目前速度最快的实现的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


大家好,我来汇报下最新进展。GoJay 发展得很顺利,目前版本是 0.10.6,很快就要发布 1.0.0 版本了。它仍然是目前最快的 JSON 解码器/编码器。我还基于 GoJay 构建了一个非常快速的 JSON 日志记录器,运行速度快且效率很高!

GitHub

头像

francoispqt/onelog

onelog - 专为 Golang 设计的极简、快速、零内存分配和模块化日志记录器

我将遵循您的建议,并将其添加到README中。

对于流式处理部分我完全同意,基于这个特性我已经有了几个关于其他开源包的想法。我也会添加流式编码功能。

您说得对,我完全不反对代码生成。我的意思更多是指那些难以阅读(或几乎不可读)的生成代码,比如EasyJson或ffjson。GoJay提供了简单但非常高效的API。我们可能会编写一个生成器来为结构体、切片等生成接口实现(这应该不会太难,因为接口很小),但还不确定是否会包含在v1.0版本中。

它应该很快就能在生产环境中使用。我们计划在两周内(5月11日)发布1.0版本。当准备就绪时,我会更新这个帖子或发布新帖子!

有趣!感谢详细的回复。或许你可以把这个加到README里?比如增加一个关于背景/动机/设计选择/权衡等的章节?

我真的很喜欢流式JSON支持功能,我觉得这在其他Go JSON库中是缺失的。

我一直想研究一下新的1.10版本中的strings.Builder - 我也得花点时间看看你的实现。

关于代码生成…浏览你的库时我想到的一点是,我可以把它用于代码生成…比如我可以为一堆类型(例如从.proto文件)生成代码,让它们实现正确的GoJay接口。为超过一两个结构体编写所有GoJay接口看起来工作量很大,所以我觉得这很适合用代码生成来实现。也许之后你可以在代码库中添加一个这样的示例,看看是否有人感兴趣?

你觉得这个库距离生产环境使用还有多远?版本0.9听起来离1.0很近了哈哈,但不幸的是版本号并不是这样算的!

嗨 Mike,感谢你的回答。

这个想法是在研究其他 JSON 包时产生的。当时看到的要么是大量反射,要么是一些丑陋的静态代码生成。没有通过简单接口就能实现的方案。所以我觉得如果采用零反射的方式,既能保持高性能,又能保证可读性和易用性。

在设计方面,这是我第一次尝试开发 JSON 编码器/解码器,但之前我从事过解释器和文本解析器的工作。 对于解码部分,我的想法是采用某种即时编译解码方式,这样既能最高效地使用 io.Reader,又能只解码我们需要的内容。在开发过程中,我意识到这个设计很适合使用通道进行解码,于是尝试并实现了这个功能。 对于编码部分,采用了类似的思路,使用字符串构建器并实现零反射。编码器实际上相当简单,借鉴了 Go 1.10 的 strings.Builder 来构建字节缓冲区。我相信编码器还有改进空间(比解码器的改进空间更大),我会继续完善它。

GoJay确实是一个出色的高性能JSON编解码库,它在处理JSON数据时表现优异,特别是在需要低延迟和高吞吐量的场景下。以下是一个简单的示例,展示如何使用GoJay进行结构体的编码和解码。

首先,确保你已经安装了GoJay:

go get github.com/francoispqt/gojay

示例:结构体的编码和解码

假设我们有一个简单的结构体:

package main

import (
    "fmt"
    "github.com/francoispqt/gojay"
)

// 定义一个结构体并实现GoJay的接口
type User struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

// 实现gojay.MarshalerJSONObject接口
func (u *User) MarshalJSONObject(enc *gojay.Encoder) {
    enc.IntKey("id", u.ID)
    enc.StringKey("name", u.Name)
    enc.StringKey("email", u.Email)
}

func (u *User) IsNil() bool {
    return u == nil
}

// 实现gojay.UnmarshalerJSONObject接口
func (u *User) UnmarshalJSONObject(dec *gojay.Decoder, key string) error {
    switch key {
    case "id":
        return dec.Int(&u.ID)
    case "name":
        return dec.String(&u.Name)
    case "email":
        return dec.String(&u.Email)
    }
    return nil
}

func (u *User) NKeys() int {
    return 3
}

func main() {
    // 编码示例
    user := &User{ID: 1, Name: "Alice", Email: "alice@example.com"}
    encoded, err := gojay.MarshalJSONObject(user)
    if err != nil {
        panic(err)
    }
    fmt.Println("Encoded JSON:", string(encoded))

    // 解码示例
    jsonStr := `{"id":2,"name":"Bob","email":"bob@example.com"}`
    decodedUser := &User{}
    err = gojay.UnmarshalJSONObject([]byte(jsonStr), decodedUser)
    if err != nil {
        panic(err)
    }
    fmt.Printf("Decoded User: ID=%d, Name=%s, Email=%s\n", decodedUser.ID, decodedUser.Name, decodedUser.Email)
}

流式解码示例

GoJay还支持流式解码,这对于处理大型JSON数据或网络流非常有用。以下是一个简单的流式解码示例:

package main

import (
    "fmt"
    "strings"
    "github.com/francoispqt/gojay"
)

// 定义一个流式解码器处理数组
func main() {
    jsonStream := `[{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}]`
    decoder := gojay.NewDecoder(strings.NewReader(jsonStream))

    // 开始数组解码
    err := decoder.DecodeArray(func(dec *gojay.Decoder) error {
        user := &User{}
        if err := dec.Object(user); err != nil {
            return err
        }
        fmt.Printf("Streamed User: ID=%d, Name=%s\n", user.ID, user.Name)
        return nil
    })
    if err != nil {
        panic(err)
    }
}

这些示例展示了GoJay在编码、解码和流式处理方面的基本用法。它的API设计简洁,性能优越,适合需要高效JSON处理的Go应用。如果你有更复杂的用例,可以进一步探索其文档和API。

回到顶部