2 个不稳定版本
0.1.0 | 2024年4月12日 |
---|---|
0.0.1-alpha | 2024年4月7日 |
#555 in 编码
每月55次下载
62KB
1.5K SLoC
serde-sqlite-jsonb
此crate提供了一个用于SQLite JSONB列的自定义Serde反序列化器。
它最初是为包含在SQLPage网站构建器中而开发的。
原因
从版本3.45.0开始,SQLite支持JSONB列,可以将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
功能已启用,此crate在尝试解析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](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) |
类型
类型 | 十六进制代码 | 描述 |
---|---|---|
空 |
0x0 | 元素是JSON "null"。 |
真 |
0x1 | 元素是JSON "true"。 |
假 |
0x2 | 元素是JSON "false"。 |
整型 |
0x3 | 元素是JSON整数,采用RFC 8259规范的标准格式。 |
整型5 |
0x4 | 元素是JSON5整数,例如0xABC 。 |
浮点型 |
0x5 | 元素是JSON浮点值,采用RFC 8259规范的标准格式。 |
浮点型5 |
0x6 | 元素是JSON5浮点值,它不是标准JSON格式。 |
文本 |
0x7 | 元素是JSON字符串值,不包含任何转义字符。 |
文本J |
0x8 | 元素是包含RFC 8259字符转义的JSON字符串值。 |
文本5 |
0x9 | 元素是包含字符转义的JSON5字符串值,包括一些来自JSON5的。 |
文本原始 |
0xA | 元素是包含需要转义于JSON中的UTF8字符的JSON字符串值。 |
数组 |
0xB | 元素是JSON数组。第一个数组元素的头部紧接在数组头部之后。 |
对象 |
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