#json #sqlite #jsonb #binary-format #serde #json-format #serializer-deserializer

fifthtry-serde_sqlite_jsonb

SQLite JSONB二进制格式的序列化和反序列化器(FifthTry分支,直到PR合并)

1个不稳定版本

0.1.0 2024年4月27日

#1003编码

MIT/Apache

62KB
1.5K SLoC

serde-sqlite-jsonb

此crate提供了一个用于SQLite JSONB列的自定义Serde反序列化器。

最初是为包含在SQLPage网站构建器而开发的。

原因

从版本3.45.0开始,SQLite支持JSONB列,可以以比JSON更高效的二进制格式存储JSON数据。

问题是,目前使用SQLite的应用程序需要将数据从JSONB转换为JSON,然后再从JSON转换为它们自己的数据结构以使用它。这阻止了使用SQLite的blob流API以流式方式直接从数据库读取blob数据,并且需要执行SQL查询以提取和转换数据到JSON。

此crate提供了一个直接针对JSONB的自定义Serde序列化和反序列化器,这允许跳过JSON转换步骤。

这可能导致某些场景下性能的显著提升,如该crate的基准测试所示。

基准测试

这些图表显示了以下时间

  • 使用此crate直接将JSONB列反序列化为结构体
  • 执行SQL查询以提取JSONB列作为JSON,然后使用serde_json将其反序列化为结构体

正在反序列化的数据包含一个字符串,其长度从50到1000个字符不等,以演示随着数据大小的变化而变化的性能。

Benchmark results Benchmark results

免责声明:这些基准测试应始终谨慎对待。当性能很重要时,您应该使用您自己的数据和应用程序来测量性能。 serde_json 非常优化,在某些场景中可能比此crate更快,特别是当JSON数据较小时。

crate功能

二进制格式可以包含原始json数据,因此此crate依赖于 serde_json crate来解析JSON数据。由于SQLite也支持json5,如果需要json5支持,可以使用 serde-json5 功能。

默认情况下,(更快) 的 serde_json 功能被启用,并且当尝试解析json5数据时会返回错误。要启用json5支持,请启用 serde-json5 功能(可选地禁用默认功能以使用json5解析器,即使对于json数据也是如此)

[dependencies]
serde-sqlite-jsonb = { version = "0.1", features = ["serde-json5"], default-features = false }

使用方法

这个库不处理SQLite连接,因此您需要使用类似 rusqlitesqlx 的crate来与数据库交互。

一旦您从数据库中提取了JSONB数据,无论是作为 Vec<u8> 或作为从数据库流式传输BLOB数据的 std::io::Read 对象,您就可以使用 serde_sqlite_jsonb crate来反序列化JSON数据,无论是到您自己的数据结构还是到 serde_json::Value

从查询结果反序列化JSONB

let conn = rusqlite::Connection::open_in_memory()?;
let blob: Vec<u8> = conn.query_row(
    r#"select jsonb('{"id": 1, "name": "John Doe"}')"#, [], |row| row.get(0),
)?;
let person: Person = serde_sqlite_jsonb::from_bytes(&blob).unwrap();

从SQLite BLOB流式反序列化

let my_blob = conn.blob_open( // returns an object that implements std::io::Read
    DatabaseName::Main,
    "my_table", // table name
    "my_jsonb_column", // column name
    42, // primary key (rowid)
    true // read-only
)?;
let parsed: serde_json::Value = // or any other type that implements Deserialize
    serde_sqlite_jsonb::from_reader(my_blob).unwrap();

格式

JSONB列的格式在SQLite文档中描述: https://sqlite.ac.cn/draft/jsonb.html

数据格式是一个带有头和有效负载的二进制格式。头包含有关元素类型和有效负载大小的信息。有效负载包含实际数据。

以下是一个粗略的ASCII表示

bits:  0  1  2  3  4  5  6  7  8
    +-------------+-------------+
    |  size(4)    | type(4)     | first header byte
    +-------------+-------------+
    |   payload size (0 - 64)   | header bytes number 2 to 9
    +---------------------------+
    |   payload data            | payload bytes (JSON strings or numbers in text format)
    +---------------------------+

有效负载大小

如果有效负载数据在0到11字节(包含)之间,则大小编码在头的第一个4位中。否则,有效负载的大小编码在接下来的字节中,第一个4位表示用于编码有效负载大小的字节数,使用以下表格

有效负载数据大小范围 大小编码 头的第一个4位
0到11字节 u4(嵌入在第一个4位中) 0到11(0x0到0xB)
12到2^8 - 1字节 u8 12(0xC)
2^8到2^16 - 1字节 u16 13(0xD)
2^16到2^32 - 1字节 u32 14(0xE)
2^32到2^64 - 1字节 u64 15(0xF)

类型

类型 十六进制代码 描述
Null 0x0 元素是JSON "null"。
True 0x1 元素是JSON "true"。
False 0x2 元素是JSON "false"。
Int 0x3 元素是按RFC 8259规范编写的JSON整数值。
Int5 0x4 元素是JSON5整数,例如 0xABC
Float 0x5 元素是按RFC 8259规范编写的JSON浮点值。
Float5 0x6 元素是格式不规范的JSON5浮点值。
Text 0x7 元素是未包含任何转义序列的JSON字符串值。
TextJ 0x8 元素是包含RFC 8259字符转义的JSON字符串值。
Text5 0x9 元素是包含字符转义,包括一些JSON5的JSON5字符串值。
TextRaw 0xA 元素是包含需要在JSON中转义的UTF8字符的JSON字符串值。
Array 0xB 元素是JSON数组。第一个数组元素的头部紧随数组头部开始。
Object 0xC 该元素是一个JSON对象。对象键(字符串)和值在有效载荷中交替。
保留13 0xD 为未来扩展保留。
保留14 0xE 为未来扩展保留。
保留15 0xF 为未来扩展保留。

示例

以下JSON对象

{"a": false, "b":true}

被编码为以下7字节的二进制数据

6c 17 61 02 17 62 01
字节 描述
0 0x6c 头部:有效载荷大小 = 6,类型 = 对象(0xC)
1 0x17 头部:有效载荷大小 = 1,类型 = 文本(0x7)
2 0x61 有效载荷:'a'
3 0x02 头部:有效载荷大小 = 0,类型 = 假(0x2)
4 0x17 头部:有效载荷大小 = 1,类型 = 文本(0x7)
5 0x62 有效载荷:'b'
6 0x01 头部:有效载荷大小 = 0,类型 = 真(0x1)

MSRV

需要rust >= 1.63(debian稳定版)

依赖项

~0.5–1.3MB
~26K SLoC