Serde
シリアライゼーションライブラリ
Serde
概要
Serdeは、Rustにおけるデファクトスタンダードのシリアライゼーション・デシリアライゼーションフレームワークです。derive macroを使用して、Rustのデータ構造を様々なフォーマット(JSON、YAML、TOML、Bincode、MessagePackなど)に効率的かつ型安全に変換できます。ゼロコスト抽象化により、高パフォーマンスを実現しています。
詳細
Serdeは「Serialization(シリアライゼーション)」と「Deserialization(デシリアライゼーション)」を組み合わせた名前で、Rustのデータ構造を効率的かつ汎用的にシリアライズ・デシリアライズするためのフレームワークです。
主な特徴:
- Derive Macro:
#[derive(Serialize, Deserialize)]を使用した自動実装 - フォーマット非依存: 同じコードで複数のデータフォーマットをサポート
- ゼロコスト抽象化: 手書きのコードと同等のパフォーマンス
- カスタマイズ可能: 属性マクロによる細かい制御
技術的詳細:
- トレイトベース設計:
SerializeとDeserializeトレイトを中心とした設計 - ビジター パターン: 効率的なデシリアライゼーションの実現
- 型安全性: コンパイル時の型チェックによる安全性保証
- ジェネリック対応: 複雑なジェネリック型や trait bound にも対応
サポートフォーマット:
- JSON (serde_json)
- YAML (serde_yaml)
- TOML (toml)
- Bincode (bincode)
- MessagePack (rmp-serde)
- CBOR (serde_cbor)
- その他多数
メリット・デメリット
メリット
- Rustエコシステムにおける標準的な選択肢
- derive macroによる簡潔な実装
- 高いパフォーマンスとメモリ効率
- 豊富なカスタマイズオプション
- 多様なデータフォーマットのサポート
- 活発なコミュニティと豊富なドキュメント
デメリット
- 学習曲線がやや急(特に高度なカスタマイズ)
- コンパイル時間の増加(derive macro使用時)
- エラーメッセージが複雑になることがある
- 一部の複雑な型変換には手動実装が必要
参考ページ
- 公式サイト: https://serde.rs/
- GitHubリポジトリ: https://github.com/serde-rs/serde
- ドキュメント: https://docs.rs/serde
- The Serde Book: https://serde.rs/
書き方の例
基本的な使用方法
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let point = Point { x: 1, y: 2 };
// JSONへのシリアライズ
let serialized = serde_json::to_string(&point).unwrap();
println!("serialized = {}", serialized);
// JSONからのデシリアライズ
let deserialized: Point = serde_json::from_str(&serialized).unwrap();
println!("deserialized = {:?}", deserialized);
}
複雑な構造体のシリアライズ
use serde::{Serialize, Deserialize};
use std::collections::HashMap;
#[derive(Serialize, Deserialize, Debug)]
struct User {
id: u64,
name: String,
email: String,
active: bool,
metadata: HashMap<String, String>,
}
#[derive(Serialize, Deserialize, Debug)]
struct Team {
name: String,
members: Vec<User>,
}
fn main() {
let mut metadata = HashMap::new();
metadata.insert("role".to_string(), "admin".to_string());
let user = User {
id: 1,
name: "Alice".to_string(),
email: "[email protected]".to_string(),
active: true,
metadata,
};
let team = Team {
name: "Development".to_string(),
members: vec![user],
};
// Pretty JSONとしてシリアライズ
let json = serde_json::to_string_pretty(&team).unwrap();
println!("{}", json);
}
カスタム属性の使用
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
struct ApiResponse {
#[serde(rename = "responseCode")]
response_code: u32,
#[serde(rename = "responseMessage")]
response_message: String,
#[serde(skip_serializing_if = "Option::is_none")]
data: Option<String>,
#[serde(default)]
timestamp: u64,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct Product {
product_id: u64,
product_name: String,
unit_price: f64,
in_stock: bool,
}
fn main() {
let product = Product {
product_id: 123,
product_name: "Laptop".to_string(),
unit_price: 999.99,
in_stock: true,
};
// camelCaseでシリアライズされる
let json = serde_json::to_string(&product).unwrap();
println!("{}", json);
// {"productId":123,"productName":"Laptop","unitPrice":999.99,"inStock":true}
}
Enumのシリアライズ
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "type")]
enum Message {
Text { content: String },
Image { url: String, alt_text: String },
Location { lat: f64, lon: f64 },
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(untagged)]
enum Value {
Null,
Bool(bool),
Number(f64),
String(String),
Array(Vec<Value>),
}
fn main() {
let messages = vec![
Message::Text { content: "Hello!".to_string() },
Message::Image {
url: "https://example.com/image.png".to_string(),
alt_text: "Example image".to_string()
},
Message::Location { lat: 35.6762, lon: 139.6503 },
];
let json = serde_json::to_string_pretty(&messages).unwrap();
println!("{}", json);
}
カスタムシリアライザーの実装
use serde::{Serialize, Serializer, Deserialize, Deserializer};
use serde::de::{self, Visitor};
use std::fmt;
// Unix timestampを人間が読める形式でシリアライズ
struct DateTimeWrapper(chrono::DateTime<chrono::Utc>);
impl Serialize for DateTimeWrapper {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let formatted = self.0.format("%Y-%m-%d %H:%M:%S").to_string();
serializer.serialize_str(&formatted)
}
}
// カスタムデシリアライザー
struct DateTimeVisitor;
impl<'de> Visitor<'de> for DateTimeVisitor {
type Value = DateTimeWrapper;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a formatted date time string")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
match chrono::DateTime::parse_from_str(value, "%Y-%m-%d %H:%M:%S %z") {
Ok(dt) => Ok(DateTimeWrapper(dt.with_timezone(&chrono::Utc))),
Err(_) => Err(E::custom("invalid date format")),
}
}
}
impl<'de> Deserialize<'de> for DateTimeWrapper {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(DateTimeVisitor)
}
}
異なるフォーマット間の変換
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
struct Config {
server: ServerConfig,
database: DatabaseConfig,
}
#[derive(Serialize, Deserialize, Debug)]
struct ServerConfig {
host: String,
port: u16,
workers: usize,
}
#[derive(Serialize, Deserialize, Debug)]
struct DatabaseConfig {
url: String,
max_connections: u32,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = Config {
server: ServerConfig {
host: "localhost".to_string(),
port: 8080,
workers: 4,
},
database: DatabaseConfig {
url: "postgres://localhost/mydb".to_string(),
max_connections: 10,
},
};
// JSONへシリアライズ
let json = serde_json::to_string_pretty(&config)?;
println!("JSON:\n{}\n", json);
// TOMLへシリアライズ
let toml = toml::to_string_pretty(&config)?;
println!("TOML:\n{}\n", toml);
// YAMLへシリアライズ
let yaml = serde_yaml::to_string(&config)?;
println!("YAML:\n{}", yaml);
Ok(())
}