1个不稳定版本
0.1.0 | 2024年4月27日 |
---|
#1003 在 编码
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个字符不等,以演示随着数据大小的变化而变化的性能。
免责声明:这些基准测试应始终谨慎对待。当性能很重要时,您应该使用您自己的数据和应用程序来测量性能。
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连接,因此您需要使用类似 rusqlite
或 sqlx
的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