Crates.io | serde_rusqlite |
lib.rs | serde_rusqlite |
version | 0.40.0 |
created_at | 2017-06-01 09:10:11.479621+00 |
updated_at | 2025-07-07 13:24:58.108178+00 |
description | Serialize/deserialize rusqlite rows |
homepage | |
repository | https://github.com/twistedfall/serde_rusqlite |
max_upload_size | |
id | 17167 |
size | 84,714 |
Support the project | Documentation
Run:
cargo add serde_rusqlite
Or add to your Cargo.toml:
[dependencies]
serde_rusqlite = "0.40.0"
This crate provides convenience functions to bridge serde and rusqlite
. With their help
you can "deserialize" [rusqlite::Row]'s into [serde::Deserialize] types and "serialize" types
implementing [serde::Serialize] into bound query arguments (positional or named) that rusqlite
expects.
Serialization of named bound arguments is only supported from struct
s and map
s because other
serde types lack column name information. Likewise, serialization of positional bound arguments
is only supported from tuple
s, sequence
s and primitive non-iterable types. In the latter case
the result will be a single-element vector. Each serialized field or element must implement
[rusqlite::types::ToSql].
For deserialization, you can use two families of functions: from_*()
and from_*_with_columns()
.
The most used one is the former. The latter allows you to specify column names for types that need
them but don't supply them. This includes different Map
types like [std::collections::HashMap].
Specifying columns for deserialization into e.g. struct
doesn't have any effect as the field list
of the struct itself will be used in any case.
SQLite only supports 5 types: NULL
([None]), INTEGER
([i64]), REAL
([f64]), TEXT
([String])
and BLOB
([Vec
Some types employ non-trivial handling, these are described below:
Serialization of u64
will fail if it can't be represented by i64
due to the SQLite limitations.
Simple enum
s will be serialized as strings so:
enum Gender {
M,
F,
}
will have two possible TEXT
options in the database "M" and "F". Deserialization into enum
from TEXT
is also supported.
[bool]s are serialized as INTEGER
s 0 or 1, can be deserialized from INTEGER
and REAL
where
0 and 0.0 are false
, anything else is true
.
[f64] and [f32] values of NaN
are serialized as NULL
s. When deserializing such a value, [OptionNaN
. The same applies to [f32].
[serde_bytes::Bytes], [serde_bytes::ByteBuf] are supported as optimized way of handling BLOB
s.
unit
serializes to NULL
.
Only sequence
s of [u8] are serialized and deserialized, BLOB
database type is used. It's
more optimal, though, to use [serde_bytes::Bytes] and [serde_bytes::ByteBuf] for such fields.
unit_struct
serializes to struct
name as TEXT
. When deserializing, the check is made to ensure
that the struct
name coincides with the string in the database.
use serde_derive::{Deserialize, Serialize};
use serde_rusqlite::*;
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct Example {
id: i64,
name: String,
}
let connection = rusqlite::Connection::open_in_memory().unwrap();
connection.execute("CREATE TABLE example (id INT, name TEXT)", []).unwrap();
// using structure to generate named bound query arguments
let row1 = Example { id: 1, name: "first name".into() };
connection.execute("INSERT INTO example (id, name) VALUES (:id, :name)", to_params_named(&row1).unwrap().to_slice().as_slice()).unwrap();
// and limiting the set of fields that are to be serialized
let row2 = Example { id: 10, name: "second name".into() };
connection.execute("INSERT INTO example (id, name) VALUES (2, :name)", to_params_named_with_fields(&row2, &["name"]).unwrap().to_slice().as_slice()).unwrap();
// using tuple to generate positional bound query arguments
let row2 = (3, "third name");
connection.execute("INSERT INTO example (id, name) VALUES (?, ?)", to_params(&row2).unwrap()).unwrap();
// deserializing using query() and from_rows(), the most efficient way
let mut statement = connection.prepare("SELECT * FROM example").unwrap();
let mut res = from_rows::<Example>(statement.query([]).unwrap());
assert_eq!(res.next().unwrap().unwrap(), row1);
assert_eq!(res.next().unwrap().unwrap(), Example { id: 2, name: "second name".into() });
// deserializing using query_and_then() and from_row(), incurs extra overhead in from_row() call
let mut statement = connection.prepare("SELECT * FROM example").unwrap();
let mut rows = statement.query_and_then([], from_row::<Example>).unwrap();
assert_eq!(rows.next().unwrap().unwrap(), row1);
assert_eq!(rows.next().unwrap().unwrap(), Example { id: 2, name: "second name".into() });
// deserializing using query_and_then() and from_row_with_columns(), better performance than from_row()
let mut statement = connection.prepare("SELECT * FROM example").unwrap();
let columns = columns_from_statement(&statement);
let mut rows = statement.query_and_then([], |row| from_row_with_columns::<Example>(row, &columns)).unwrap();
assert_eq!(rows.next().unwrap().unwrap(), row1);
assert_eq!(rows.next().unwrap().unwrap(), Example { id: 2, name: "second name".into() });
// deserializing using query() and from_rows_ref()
let mut statement = connection.prepare("SELECT * FROM example").unwrap();
let mut rows = statement.query([]).unwrap();
{
// only the first record is deserialized here
let mut res = from_rows_ref::<Example>(&mut rows);
assert_eq!(res.next().unwrap().unwrap(), row1);
}
// the second record is deserialized using the original Rows iterator
assert_eq!(from_row::<Example>(&rows.next().unwrap().unwrap()).unwrap(), Example { id: 2, name: "second name".into() });
MIT OR Apache-2.0