Serde
serde
是一个用于高效且通用地序列化Serialize和反序列化Deserialize Rust 数据结构的框架。另外,serde
主要关注于如何将Rust结构体(struct)轻松且准确地映射到serde
的内部数据模型,而将serde
的内部数据模型与实际的文件格式进行转换的过程,不是serde
库本身完成的,而是类似于serde-json
这种库完成的。
使用方式
参考Using derive · Serde。对于一般的情况(如反序列化个JSON文件),只需要简单的定一个一个数据结构,添加个宏就够了。以JSON为例:
在解析 JSON 文本的时候,我们可能有两种需求,以及相反的序列化的需求。
- 解析成一个 strongly typed Rust data structure。比如一些 API 已经规定好了返回的数据格式。
- 解析成一个 untyped or loosely typed representation。一些更普通的情况。
Demo For Strong Type: 我们一般使用 #[derive(Serialize, Deserialize)]
来修饰要序列化/反序列化的类型。然后在 serde_json::from_str
的时候指定类型。
use serde::{Deserialize, Serialize};
use serde_json::Result;
#[derive(Serialize, Deserialize)]
struct Person {
name: String,
age: u8,
phones: Vec<String>,
}
fn typed_example() -> Result<()> {
// Some JSON input data as a &str. Maybe this comes from the user.
let data = r#"
{
"name": "John Doe",
"age": 43,
"phones": [
"+44 1234567",
"+44 2345678"
]
}"#;
// Parse the string of data into a Person object. This is exactly the
// same function as the one that produced serde_json::Value above, but
// now we are asking it for a Person as output.
let p: Person = serde_json::from_str(data)?;
// Do things just like with any other Rust data structure.
println!("Please call {} at the number {}", p.name, p.phones[0]);
Ok(())
}
如果我们希望再将 Person 序列化成 json 文本,可以调用 serde_json::to_string
// Serialize it to a JSON string.
let j = serde_json::to_string(&p)?;
// Print, write to a file, or send to an HTTP server.
println!("{}", j);
Demo For Loose Type: serde_json::Value
是这种松散类型的一般表示。在 serde_json::from_str
的时候指定类型为 Value 即可。
use serde_json::{Result, Value};
fn untyped_example() -> Result<()> {
// Some JSON input data as a &str. Maybe this comes from the user.
let data = r#"
{
"name": "John Doe",
"age": 43,
"phones": [
"+44 1234567",
"+44 2345678"
]
}"#;
// Parse the string of data into serde_json::Value.
let v: Value = serde_json::from_str(data)?;
// Access parts of the data by indexing with square brackets.
println!("Please call {} at the number {}", v["name"], v["phones"][0]);
Ok(())
}
不难想到,序列化就是先构造松散类型的 Value
类型数据,再 to_string
。我们可以使用 json!
宏来构造 Value
类型数据。
use serde_json::json;
fn main() {
// The type of `john` is `serde_json::Value`
let john = json!({
"name": "John Doe",
"age": 43,
"phones": [
"+44 1234567",
"+44 2345678"
]
});
println!("first phone number: {}", john["phones"][0]);
// Convert to a string of JSON and print it out
println!("{}", john.to_string());
}
工作原理
序列化
根据库的设计,对一个对象(value
)进行序列化的基本方式为:
// 创建一个新的 序列化器,一般会传入一个`io::Write`用来写序列化的结果
let mut ser = Serializer::new(writer);
// 将 value 序列化的结果保存到 ser 中,一般就是写到相应的writer中
value.serialize(&mut ser)?;
serialize
是trait ser::Serialize
中定义的唯一的方法,他会根据用户传入的序列化器,对对象进行序列化操作。一般来说,对象的Serialize方法都是使用#derive(Serialize)
自动生成的。这也就意味着,传入的序列化器必须要有良好的抽象,才能在此基础上为所有的结构自动生成serialize
方法。
pub trait Serialize {
/// Serializes this value into this serializer.
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where S: Serializer;
}
pub trait Serializer: Sized {
...
fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error>;
...
}
序列化器要满足pub trait Serializer
,它包含了对Rust Type System中29种元素的处理方式。例如,serialize_bool
,以JSON为例,不难猜测序列化器在实现这个接口时,会将v
变成true
或false
的字符串。
由此,我们不难猜测——自动生成的serialize
方法,其实规定了遍历对象的顺序,根据遍历对象的类型递归的调用每个类型相应的方法?由于宏的代码太难读懂了,不妨写一个简单的代码看宏的展开结果(使用cargo-expand
)。结论是思路差不多。
整个序列化的过程是一个状态机的思路,状态机的代码一般由#derive(Serialize)
自动生成,状态->状态之间的转化规则也是。而每个状态转化过程中,造成的副作用,则是由用户的序列化器决定。
反序列化
反序列化的过程似乎没有那么直白。毕竟序列化面对的是Rust的规整数据结构,反序列化面对的则是字节串。类似的,我们从基本用法入手:
乍一看,懵了。为什么和序列化不一样?怎么光有反序列化器没有数据?其实数据一般通过io::Read
读入。接着调用deserialize
方法反序列化成Rust的数据结构。类似的,这个方法一般也是通过#derive(Deserialize)
自动生成的。
pub trait Deserialize<'de>: Sized {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de>;
}
pub trait Deserializer<'de>: Sized {
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>;
/// Hint that the `Deserialize` type is expecting a `bool` value.
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>;
...
}
反序列化器要满足pub trait Deserializer
,它也规定了对Rust Type System中29种元素的处理方式。
每一个方法都要传入一个Visitor
,返回一个Visitor::Value
,Visitor的作用就是给定数据,返回Rust类型。不难猜测,对于JSON来说,当遇到"true"
的时候,返回的大概就是Visitor::Value::Bool(True)
。如果我们知道我们的JSON就包含一个bool
,那就调用deserialize_bool
。
类似的,反序列化的过程也是一个状态机的思路。状态机的状态转移由#derive(Deserialize)
自动生成。我还没有特别搞明白。
编写自定义规则
NOM
nom是一个解析器组合器库,专注于安全解析、流模式和尽可能多的零复制。