Serde

シリアライゼーションRustデシリアライゼーションderive-macro型安全

シリアライゼーションライブラリ

Serde

概要

Serdeは、Rustにおけるデファクトスタンダードのシリアライゼーション・デシリアライゼーションフレームワークです。derive macroを使用して、Rustのデータ構造を様々なフォーマット(JSON、YAML、TOML、Bincode、MessagePackなど)に効率的かつ型安全に変換できます。ゼロコスト抽象化により、高パフォーマンスを実現しています。

詳細

Serdeは「Serialization(シリアライゼーション)」と「Deserialization(デシリアライゼーション)」を組み合わせた名前で、Rustのデータ構造を効率的かつ汎用的にシリアライズ・デシリアライズするためのフレームワークです。

主な特徴:

  • Derive Macro: #[derive(Serialize, Deserialize)]を使用した自動実装
  • フォーマット非依存: 同じコードで複数のデータフォーマットをサポート
  • ゼロコスト抽象化: 手書きのコードと同等のパフォーマンス
  • カスタマイズ可能: 属性マクロによる細かい制御

技術的詳細:

  • トレイトベース設計: SerializeDeserializeトレイトを中心とした設計
  • ビジター パターン: 効率的なデシリアライゼーションの実現
  • 型安全性: コンパイル時の型チェックによる安全性保証
  • ジェネリック対応: 複雑なジェネリック型や trait bound にも対応

サポートフォーマット:

  • JSON (serde_json)
  • YAML (serde_yaml)
  • TOML (toml)
  • Bincode (bincode)
  • MessagePack (rmp-serde)
  • CBOR (serde_cbor)
  • その他多数

メリット・デメリット

メリット

  • Rustエコシステムにおける標準的な選択肢
  • derive macroによる簡潔な実装
  • 高いパフォーマンスとメモリ効率
  • 豊富なカスタマイズオプション
  • 多様なデータフォーマットのサポート
  • 活発なコミュニティと豊富なドキュメント

デメリット

  • 学習曲線がやや急(特に高度なカスタマイズ)
  • コンパイル時間の増加(derive macro使用時)
  • エラーメッセージが複雑になることがある
  • 一部の複雑な型変換には手動実装が必要

参考ページ

書き方の例

基本的な使用方法

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(())
}