Rustにおける複雑なデータ構造のためのカスタムシリアライゼーションのマスター
Lukas Schneider
DevOps Engineer · Leapcell

はじめに
ネットワークアプリケーションやデータ永続化の世界では、構造化データを送信または保存に適した形式に変換し、それを元の形式に再構築する能力は不可欠です。シリアライゼーションとデシリアライゼーションとして知られるこのプロセスは、現代のソフトウェア開発の礎です。Rustのserdeフレームワークは、ほとんどの一般的なデータ型に対して強力でしばしば自動的な派生を提供しますが、私たちのデータモデルが単純な構造体や列挙型から逸脱する必然的なシナリオが存在します。カスタム検証ロジックをデシリアライゼーション中に処理する場合、シリアライゼーションのための標準外のデータレイアウト、または異常なデータ形式を指示する外部APIとの対話など、複雑な型を扱う場合、デフォルトの#[derive(Serialize, Deserialize)]マクロでは不十分です。ここでserdeの真の力が発揮されます。serde::Serializeおよびserde::Deserializeのカスタム実装を作成することです。これらのトレイトを手動で実装する方法を理解することは、開発者が事実上あらゆるシリアライゼーションの課題に取り組むことを可能にし、データの整合性と相互運用性を保証します。
serdeカスタマイズのコアコンセプト
実装の詳細に入る前に、カスタムserde実装に関わる主要な概念の基本的な理解を確立しましょう。
serde::Serializeトレイト: このトレイトは、Rust型がserdeによって理解される中間データ形式にどのように変換されるかを定義します。これは、Serializerを引数として取るserializeという単一のメソッドを必要とします。Serializerは、異なるデータプリミティブ(整数、文字列、ブール値、配列、マップなど)を書き出す方法を知っているserdeによって提供される抽象インターフェースです。あなたの実装は、Serializerにあなたの型の内部構造をどのように表現するかを指示します。
serde::Deserializeトレイト: このトレイトは、Rust型が中間データ形式からどのように構築されるかを定義します。これは、Deserializerを引数として取るdeserializeという単一のメソッドを必要とします。Deserializerは、異なるデータプリミティブを読み取るメソッドを提供する抽象インターフェースです。あなたの実装は、Deserializerを使用してデータを抽出し、あなたの型のインスタンスを構築します。しばしば「ビジター」パターンを使用します。
serde::Serializer: このトレイトは通常、出力形式(例:JSON、YAML、Bincode)を表します。serialize_i32、serialize_str、serialize_struct、serialize_seqなどのメソッドを提供し、これらはSerialize実装によってデータを書き出すために呼び出されます。
serde::Deserializer: このトレイトも入力形式を表します。deserialize_i32、deserialize_string、deserialize_struct、deserialize_seqなどのメソッドを提供し、これらはDeserialize実装(特にビジター)によってデータを読み取るために呼び出されます。
serde::de::Visitor: 複雑な型に対してDeserializeを実装する場合、実際の解析ロジックをVisitorに委任することがよくあります。Visitorは、異なる種類のデータ型(例:visit_i32、visit_str、visit_map、visit_seq)を処理するためのメソッドを定義するトレイトです。Deserializerは、検出したデータに応じて、あなたのVisitorで適切なvisit_メソッドを呼び出します。このパターンは、堅牢で柔軟なデシリアライゼーションロジックを可能にします。
カスタムシリアライゼーションとデシリアライゼーションの実装
これらの概念を実践的な例で説明しましょう。カルテシアン座標を格納するPoint構造体があると想像してください。しかし、特定の外部APIでは、デフォルトの構造体表現ではなく、単一の文字列「x,y」としてシリアライズし、同じ形式からデシリアライズする必要があります。
use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use std::fmt; // 私たちの複雑なデータ型:Point構造体 #[derive(Debug, PartialEq)] struct Point { x: i32, y: i32, } // Pointのためのカスタムシリアライゼーション impl Serialize for Point { fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer, { // Pointを文字列「x,y」としてシリアライズしたい let s = format!(

