`serde::Deserializer`を実装する方法 —— serde_tree_sitterを例として
はじめに
わけあってserdeのデシリアライズ処理を実装する機会があったのだが、その過程で知ったことをまとめようと思う。
serdeについてはおなじみの方が多いと思う。JSONデータから構造体を得たいときに使うあれだ。
#[derive(serde::Deserialize)]
struct Entity {
id: u64,
name: Option<String>,
}
let entity: Entity = serde_json::from_str(json_data)?;
入力文字列を構造体に自動でマッピングしてくれるというわけだ。
このライブラリのデシリアライズ処理は「何らかの入力をパースする部分(serde::Deserializer
)」と「パースした結果から何らかの値を構築する部分(serde::Deserialize
)」に分かれており、新たなデータフォーマットに対応したい場合はパース部分だけ実装すればよい。
言うは易し。
SerdeのデシリアライズAPIはなぜこんなことになっているのか
serde::Deserializer
を自作しようと思った人は、そのAPIの複雑さに目を剥くことになるだろう。パフォーマンスと汎用性を両立するための工学的努力がこのような怪物を産み出したのだが、なぜこのようなことになってしまったのか見ていこう。
一番単純なデシリアライズAPI
ある入力I
を値struct A
に変換しようと思ったら、一番単純なのは素直にそのような関数を書くことだろう:
struct A {
a: u32,
b: String,
}
fn deserialize(input: &I) -> Result<A> {
let parser = Parser::new(input);
let a = parser.parse_u32()?;
let b = parser.parse_string()?;
Ok(A { a, b})
}
特定のデータ形式をパースして特定の型を構築する関数は、慎重に作れば最高のパフォーマンスを達成する。もちろんこの方式の問題は、特定のデータフォーマットと特定の型の組み合わせにしか対応していない点だ。
データフォーマットとデータ型を切り離し、個別に実装したいときはどうすればよいだろう?
中間データ型を使用した分離
All problems in computer science can be solved by another level of indirection, except for the problem of too many layers of indirection.
—— David Wheeler
中間データ型を導入することで、パースと値の構築の分離を達成できる。パース結果をSerdeData
型として返す関数と、SerdeData
型を目的の型に変換する関数を書けばよい。
enum SerdeData {
I32(i32),
I64(i64),
// ...
Struct(Map<String, SerdeData>),
Array(Vec<SerdeData>),
// ...
}
fn parse_json(src: &str) -> Result<SerdeData>;
struct A { a: u32, b: String}
fn deserialize(input: SerdeData) -> Result<A>;
fn deserialize_from_json(src: &str) -> Result<A> {
deserialize(parse_json(src)?)
}
なんということだ、another level of indirection万歳!!
外部スキーマの導入
データフォーマットには二種類ある: スキーマが内在しているものと、外部化されているものが。
たとえばJSONは前者だ。{"a": 1}
という文字列を見れば、それがオブジェクト型であり、a
というフィールドがあり、その値は1
だということがわかる。Protobufは後者で、あるバイト列が何を表現しているのかは、外部のスキーマ定義を参照しないと解決できない。
protobufのようなデータフォーマットも扱うには、パース時にヒントとしてスキーマ情報を渡してやればよい。
trait DeserializeSource {
fn parse(schema: Schema) -> Result<SerdeData>;
}
trait Deserialize {
fn deserialize(src: impl DeserializeSource) -> Result<Self>;
}
struct ProtobufParser<'s>(&'s str);
impl DeserializeSource for ProtobufParser { /* ... */}
struct A { a: u32, b: String}
impl Deserialize for A {
fn deserialize(src: impl DeserializeSource) -> Result<Self> {
let data = src.parse(Schema::Struct { a: u32, b: String})?;
// validate and construct struct A from data
}
}
fn deserialize_from_protobuf_to_a(src: &[u8]) -> Result<A> {
A::deserialize(ProtobufParser::new(src))
}
注意すべきは、スキーマ情報はあくまでもヒントだということだ。スキーマを内在しているデータフォーマットなら、入力データに基づいて要求とは違う種類の値を返すかもしれない。
何はともあれ、様々なデータフォーマットに対応したシリアライズライブラリが欲しければ、このようなAPIで実現できる。
インクリメンタルな値構築
しかし、いま見たようなシンプルな仕組みにはひとつ問題がある。一度中間データを構築した上でさらにそれを目的の値に変換する方式は、パフォーマンスの面において不利である(problem of too many layers of indirection!)。ゼロコスト・アブストラクションを理想とするRustユーザにとってこれは耐えがたい屈辱であるし、Rustにはもっとよい抽象化を実現する力がある。
JSON {a: 1, b: "foo"}
を struct Foo{a: i32, b: String}
にマッピングする例を考えてみよう。Map型を要求されたパーサは、入力の先頭が'{'
であることを確認した段階でMap(accessor)
を返す。accessor
はパーサへの参照を持ち、オブジェクト内部のデータへアクセスするインタフェースを提供する。値の構築側はaccessor
経由でキー名を取得し、名前に応じた型の値をデシリアライズする。図示すると以下のようになるだろう:
値の構築はこのようなコードになる:
fn deserialize<P: Parser>(parser: P) -> Result<A>{
match parser.read(DataType::Map)? {
Data::Map(accessor) => {
let mut a;
let mut b;
loop {
let Some(key) = accessor.next_key::<String>()? else { break; };
match key {
"a" => { a = accessor.next_value::<i32>(); }
"b" => { b = accessor.next_value::<String>(); }
_ => {}
}
if a is set && b is set { break; }
}
Ok(A {a, b} )
}
// ...
}
}
要求に応じて、必要な分だけ入力をパースしているというのがおわかりいただけるだろうか。
……お前今わかりにくいと思っただろ、俺もだよ。悪い知らせとしては、まだこの先がある。
まず上記のはあくまで擬似コードで、実際にこのような設計ができるかは未検討だ。また、仮に実現できたとして、パーサから得た結果をmatch
する時点で動的な分岐が必要になるが、これはゼロコスト・アブストラクションの信条に反するので避けたい。そこでserdeはVisitorパターンを採用した。パーサに渡す引数にvisitorを追加し、その中に「パース成功時の後続処理」を書いておく。このパターンのメリットとしては、動的な分岐が最小限で済むことだ。デメリットとしてはメソッドが30個くらいあるtraitがふたつできることがあるが、パフォーマンスのためには必要な犠牲である。
SerdeのデシリアライズAPI
ようやくここからが本番だ。serdeが採用したデシリアライズAPIの概要について述べる:
Serde data model
先ほどの説明で中間データについて述べた。Serdeが採用している中間データはSerde data modelのページに記載がある。
-
bool
,u8
, ...,u64
, ... のような基本型 Option<T>
- Tuple:
(T1, T2, ...)
- Unit struct:
struct X;
- Struct:
struct X{a: T1, b: T2, ...}
- Tuple struct:
struct X(T1, T2, ...)
- Newtype struct:
struct X(T)
- 1-ary tuple structとは区別されている。
#[derive(Deserialize)]
の実装では、1要素のtuple structはnewtype structとして処理されるようだ。 - Newtype patternの精神に基づき、
deserialize::<X(T)>() == X(deserialize::<T>())
になってほしいという気持ちが込められているのではと思う。
- 1-ary tuple structとは区別されている。
- Enum:
enum { ... }
- Unit variant:
V0
- Tuple variant:
V1(T1, T2, ...)
- Newtype variant:
V2(T)
- Struct variant: `V3 { a: T1, b: T2,
- Unit variant:
- Seq: Heterogeneousなリスト。
[T]
などだが、型の違う値を並べたやつもこれで表現する - Map: HeterogeneousなMap。
注意すべきは、APIにデータ型が全種類出てくるとは限らないという点だ。たとえば後述のserde::de::Visitor
traitには、visit_u8
やvisit_enum
はあってもvisit_struct
は存在しない。これはvisit_map
やvisit_seq
で用が足りるからだろう。各traitのメソッド一覧を十分長い時間凝視していると何かが見えると思う。
serde::Deserialize<'de>
trait
Serdeにおけるデシリアライズの主要な構成要素として、まずserde::Deserialize
がある。#[derive(Deserialize)]
で導出されるあれである。これを自分で実装することはまずないだろう。このtraitの責務は、serde::Deserializer
を使って値を構築することだ:
pub trait Deserialize<'de>: Sized {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de>;
// ...
}
パフォーマンスの観点からは、メモリ確保とデータコピーが発生するString
でなく、入力データを参照した&str
で済ませたいケースがあるだろう。ライフタイムパラメータ'de
はそのためのもので、デシリアライズ結果のライフタイムを表している。
serde::Deserializer<'de>
trait
次にserde::Deserializer
がある。よりによってserde::Deserialize
と1文字違いであるこのtraitの責務は、serde::Deserialize
の要求に応じて入力データを解析し、必要なデータを取り出すことだ。
pub trait Deserializer<'de>: Sized {
type Error: Error;
// Required methods
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where V: Visitor<'de>;
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where V: Visitor<'de>;
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where V: Visitor<'de>;
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where V: Visitor<'de>;
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where V: Visitor<'de>;
// ...
fn deserialize_enum<V>(
self,
name: &'static str,
variants: &'static [&'static str],
visitor: V
) -> Result<V::Value, Self::Error>
where V: Visitor<'de>;
fn deserialize_identifier<V>(
self,
visitor: V
) -> Result<V::Value, Self::Error>
where V: Visitor<'de>;
fn deserialize_ignored_any<V>(
self,
visitor: V
) -> Result<V::Value, Self::Error>
where V: Visitor<'de>;
// ...
うわっ多い。32メソッドあるそうです。各メソッドはserde data modelに対応しており、「この種類のデータが欲しい」という気持ちを表現している。これはあくまでもヒントなので、入力データによっては別のデータ型として処理されることもあるだろう。deserialize_any
は「値の種類は入力データにおまかせ」という気持ちを表現している。
各メソッドでは、要求に基づいて入力をパースし、その結果をvisitor.visit_*
に渡す。このDeserializer
traitこそがパース処理の中心で、新しいデータフォーマットのデシリアライズ処理を作るときはこれを実装することになる。
serde::de::Visitor
trait
このtraitの責務は、パース結果を受け取って値を構築することだ。serde::Deserialize
の実装で使用される。
これも#[derive(Deserialize)]
により導出されるため、自分で実装することはまずないだろう。serde::Deserializer
の実装の際にはこのtraitと連携することになる。
pub trait Visitor<'de>: Sized {
/// The value produced by this visitor.
type Value;
fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E> where E: Error;
fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E> where E: Error;
// ...
fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
where A: SeqAccess<'de>;
// ...
}
Serde data modelに対応したvisit_*
メソッドが定義されており、パースした値に応じてこれらのメソッドのどれかを呼ぶ。
serde::de::Error
trait
デシリアライズ時のエラーを表現する型には、このtraitを実装する必要がある。
デシリアライズ時には、二種類のエラーが発生しうる: すなわち、パース時のエラー(JSONフォーマットが壊れている!)と、値構築時のエラー(必要なフィールドが存在しない!)だ。前者についてはserde::Deserializer
の領分であり、こちらでコントロール可能だが、後者はserde::Deserialize
の内部で起こる。serde::de::Error
traitは、二種類のエラーを統合するためのものだ。
pub trait Error: Sized + StdError {
// Required method
fn custom<T>(msg: T) -> Self
where T: Display;
// Provided methods
fn invalid_type(unexp: Unexpected<'_>, exp: &dyn Expected) -> Self { ... }
fn invalid_value(unexp: Unexpected<'_>, exp: &dyn Expected) -> Self { ... }
fn invalid_length(len: usize, exp: &dyn Expected) -> Self { ... }
fn unknown_variant(variant: &str, expected: &'static [&'static str]) -> Self { ... }
fn unknown_field(field: &str, expected: &'static [&'static str]) -> Self { ... }
fn missing_field(field: &'static str) -> Self { ... }
fn duplicate_field(field: &'static str) -> Self { ... }
}
これらのメソッド群は、serde::Deserialize
内部で発生しうるエラーをserde::Deserializer
側で定義されるエラーに変換するためのものだ。
エラー型を定義する際は以下のようにする:
pub enum MyDeserializeError {
// Deserializer側で発生するエラー
FormatError,
TypeNotSupported,
// Deserialize側で発生するエラー
CustomError(String),
}
impl serde::de::Error for MyDeserializeError {
fn custom<T: Display>(msg: T) -> Self {
MyDeserializeError::CustomError(msg.into())
}
}
このtraitを実装することで、デシリアライズ時のエラーを統一して扱うことができる。
serde::de::*Access
trait
SeqAccess
, MapAccess
, EnumAccess
, VariantAccess
がある。これらのtraitの責務は、対応する種類のデータへのアクセスを提供することだ。このtraitを実装した値はserde::Deserializer
で作られ、serde::de::Visitor
に渡される。
実際の使用例はこのようになる:
impl <'de> Deserializer<'de> for MyDeserializer {
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: serde::de::Visitor<'de>,
{
// Seq型が要求された場合、入力の解釈は行わず、`SeqAccess`を作って`visitor`に渡すだけ
let seq_access = crate::access::SeqAccess::new(self.node.named_children());
visitor.visit_seq(seq_access)
}
// ...
}
// module crate::de
pub struct SeqAccess<'de, N: TsNode<'de>, I: Iterator<Item = N>> {
nodes: I,
_p: PhantomData<&'de N>,
}
impl<'de, N: TsNode<'de>, I: Iterator<Item = N>> serde::de::SeqAccess<'de>
for SeqAccess<'de, N, I>
{
type Error = crate::DeserializeError;
// SeqAccessには「Seqの次の要素を得る」APIが定義されている
// Visitor内部で必要に応じて呼ばれる
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
where
T: serde::de::DeserializeSeed<'de>,
{
let Some(n) = self.nodes.next() else {
return Ok(None);
};
let v = seed.deserialize(NodeDeserializer::new(n))?;
Ok(Some(v))
}
}
実例: serde_tree_sitter
tree-sitterとは何か、という話は本題から外れるので省略するが、そういうパーサライブラリがあってツリー構造のデータを出力する、と思ってほしい。
ツリーの各ノードには、
- 種類(kind)を表す文字列
- そのノードに対応する入力文字列
- 子要素のリスト
のデータが含まれる。
また、子要素ごとに任意でフィールド名をつけることができる(重複可)。
; ルートノード(kind=bin_op, 対応文字列="1 + 2")
(bin_op "1 + 2"
; 子要素(フィールド名=lhs, kind=int, 対応文字列="1")
lhs: (int "1")
; 子要素(フィールド名なし, kind=op, 対応文字列="+")
(op "+")
; 子要素(フィールド名=rhs, kind=int, 対応文字列="2")
rhs: (int "2"))
残念ながらこのライブラリはCで書かれているため、Rustのenum/structを出力してくれない。そのかわり、以下のようなAPIが提供されている:
pub trait TsNode<'de>: Clone {
fn named_children(&self) -> impl ExactSizeIterator<Item = Self>;
fn children_by_field_name(&self, name: &str) -> impl ExactSizeIterator<Item = Self>;
fn kind(&self) -> &'static str;
fn src(&self) -> &'de str;
}
これを任意の構造体にマッピングしてやりたいので、そのためのserde::Deserializer
を作ろうという目論見。さっそくserde::Deserializer
traitを実装していこう。
any
今回は対応していないのだが、フォーマットによっては重要なので一応触れておく。
Deserializer::deserialize_any
メソッドは、具体的なデータ構造を要求せず、入力データに沿った値を得るためのものだ。スキーマ内在のデータフォーマットを扱う際には、このメソッド内で主要なパース処理を行い、その他のメソッドはdeserialize_any
に処理を委譲すると楽かもしれない。Serdeにはそのためのforward_to_deserialize_anyというマクロが用意されている。
Unit
任意のノードは()
にマップ可能だ。すべての情報は無視される。特定の子ノードだけ無視したいときなどに使えるだろう。
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: serde::de::Visitor<'de>,
{
visitor.visit_unit()
}
bool
数値とかこれらのプリミティブ型については、ノードが表現している文字列をパースしたものを結果とする。
まずパース関数を用意して、
fn parse_int<T: std::str::FromStr<Err = std::num::ParseIntError>>(
&self,
) -> Result<T, DeserializeError> {
self.node
.src()
.parse::<T>()
.map_err(DeserializeError::ParseIntError)
}
fn parse_float<T: std::str::FromStr<Err = std::num::ParseFloatError>>(
&self,
) -> Result<T, DeserializeError> {
self.node
.src()
.parse::<T>()
.map_err(DeserializeError::ParseFloatError)
}
fn parse_bool<T: std::str::FromStr<Err = std::str::ParseBoolError>>(
&self,
) -> Result<T, DeserializeError> {
self.node
.src()
.parse::<T>()
.map_err(DeserializeError::ParseBoolError)
}
メソッド定義用マクロを作って、
macro_rules! handle_primitive {
($name:ident, $parse:ident, $visit:ident) => {
fn $name<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where V: serde::de::Visitor<'de>
{
visitor.$visit(self.$parse()?)
}
};
}
あとはそれぞれの型向けの実装を生成する。
handle_primitive!(deserialize_bool, parse_bool, visit_bool);
handle_primitive!(deserialize_u8, parse_int, visit_u8);
handle_primitive!(deserialize_u16, parse_int, visit_u16);
handle_primitive!(deserialize_u32, parse_int, visit_u32);
handle_primitive!(deserialize_u64, parse_int, visit_u64);
handle_primitive!(deserialize_i8, parse_int, visit_i8);
handle_primitive!(deserialize_i16, parse_int, visit_i16);
handle_primitive!(deserialize_i32, parse_int, visit_i32);
handle_primitive!(deserialize_i64, parse_int, visit_i64);
handle_primitive!(deserialize_f32, parse_float, visit_f32);
handle_primitive!(deserialize_f64, parse_float, visit_f64);
宣言的マクロの制限により、型を指定するだけでメソッド名を自動生成してくれるというわけにはいかないのだが、まあこのくらいの記述量ならいいでしょう。
&'de str
およびString
ノードが表現している文字列をそのまま返せばよい。
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: serde::de::Visitor<'de>,
{
visitor.visit_borrowed_str(self.node.src())
}
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: serde::de::Visitor<'de>,
{
visitor.visit_string(self.node.src().to_owned())
}
Tuple
子ノードをタプルの各要素に割り当てる。
ノードの個数をチェックしたあとはdeserialize_seq
に委譲する。この処理についてはVec<T>
の項で解説する。
fn deserialize_tuple<V>(self, len: usize, visitor: V) -> Result<V::Value, Self::Error>
where
V: serde::de::Visitor<'de>,
{
if len != self.node.named_child_count() {
return Err(DeserializeError::ChildCount {
expected: len,
actual: self.node.named_child_count(),
});
}
self.deserialize_seq(visitor)
}
Vec<T>
任意長の子ノードをそれぞれデシリアライズする。
deserialize_seq
が呼ばれた段階では、各要素に対する具体的な処理が不明であるため、SeqAccess
traitを実装した値をvisitor
に渡してやる。Visitor側で、必要に応じて要素へのアクセスを行うことになる。
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: serde::de::Visitor<'de>,
{
let seq_access = crate::access::SeqAccess::new(self.node.named_children());
visitor.visit_seq(seq_access)
}
SeqAccess
にはnext_element
メソッドが定義されており、次の値をインクリメンタルに読むことができる。要素毎に違う型を指定することができるため、Vec<T>
もtupleもこのインタフェースで対応可能だ。
next_element_seed
は、元となる値(seed)から目的の値を作るための仕組みだが、これは詳しく調べていない。
pub struct SeqAccess<'de, N: TsNode<'de>, I: Iterator<Item = N>> {
nodes: I,
_p: PhantomData<&'de N>,
}
impl<'de, N: TsNode<'de>, I: Iterator<Item = N>> serde::de::SeqAccess<'de>
for SeqAccess<'de, N, I>
{
type Error = crate::DeserializeError;
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
where
T: serde::de::DeserializeSeed<'de>,
{
let Some(n) = self.nodes.next() else {
return Ok(None);
};
let v = seed.deserialize(NodeDeserializer::new(n))?;
Ok(Some(v))
}
}
Struct
- ノードの種類がstructの名前と一致していることを確認
- 各フィールドの内容をシーケンスとして返す
FieldsAsSeqAccess
をvisitorに渡す。シーケンスの順序は、deserialize_struct
に渡されたフィールド名の順序。
fn deserialize_struct<V>(
self,
name: &'static str,
fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: serde::de::Visitor<'de>,
{
if name != self.node.kind() {
return Err(DeserializeError::node_type(name, self.node.kind()));
}
visitor.visit_seq(FieldsAsSeqAccess::new(self.node, fields))
}
impl<'de, N: TsNode<'de>> serde::de::SeqAccess<'de> for FieldsAsSeqAccess<'de, N> {
type Error = DeserializeError;
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
where
T: serde::de::DeserializeSeed<'de>,
{
if self.fields.len() <= self.index {
return Ok(None);
}
let field = self.fields[self.index];
self.index += 1;
let nodes = self.node.children_by_field_name(field);
seed.deserialize(crate::deserializer::FieldDeserializer::new(
field,
nodes.collect(),
))
.map(Some)
}
}
フィールドは複数のノードを含みうるので、単一ノードのものとはデシリアライズのルールが異なる。FieldDeserializer
型を作ってそちらに委譲した。
enum
Enumの処理は少々複雑だ。deserialize_enum
ではVisitorにEnumAccess
を渡すだけで、主要な処理はこの型に実装する。
fn deserialize_enum<V>(
self,
name: &'static str,
variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: serde::de::Visitor<'de>,
{
let enum_access = crate::access::EnumAccess::new(self.node);
visitor.visit_enum(enum_access)
}
EnumAccess::variant_seed
が返すのは、バリアントを識別するための値(一般的には、バリアント名を表す文字列になるだろう)と、バリアントを構築するためのVariantAccess
traitを実装した値だ。
impl<'de, N: TsNode<'de>> serde::de::EnumAccess<'de> for EnumAccess<'de, N> {
type Error = DeserializeError;
type Variant = VariantAccess<'de, N>;
fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error>
where
V: serde::de::DeserializeSeed<'de>,
{
let value = seed.deserialize(NodeDeserializer::new(self.node.clone()))?;
let variant_access = VariantAccess::new(self.node);
Ok((value, variant_access))
}
}
VariantAccess
traitの実装では、バリアントの種類(Unit/Struct/Newtype/Tuple)に応じて、値を直接構築するかvisitorにaccessを渡す。
impl<'de, N: TsNode<'de>> serde::de::VariantAccess<'de> for VariantAccess<'de, N> {
type Error = DeserializeError;
fn unit_variant(self) -> Result<(), Self::Error> {
Ok(())
}
fn newtype_variant_seed<T>(self, _seed: T) -> Result<T::Value, Self::Error>
where
T: serde::de::DeserializeSeed<'de>,
{
_seed.deserialize(crate::NodeDeserializer::new(self.node))
}
fn tuple_variant<V>(self, _len: usize, _visitor: V) -> Result<V::Value, Self::Error>
where
V: serde::de::Visitor<'de>,
{
if self.node.named_child_count() != _len {
return Err(DeserializeError::ChildCount {
expected: _len,
actual: self.node.named_child_count(),
});
}
let seq = SeqAccess::new(self.node.named_children());
_visitor.visit_seq(seq)
}
fn struct_variant<V>(
self,
_fields: &'static [&'static str],
_visitor: V,
) -> Result<V::Value, Self::Error>
where
V: serde::de::Visitor<'de>,
{
_visitor.visit_seq(FieldsAsSeqAccess {
node: self.node,
fields: _fields,
index: 0,
_p: PhantomData,
})
}
}
エントリーポイント
必要に応じて、デシリアライズ用の関数を提供するのがようだろう。たとえばserde_jsonにはfrom_str<'de, D: Deserialize<'de>>(json: &'de str) -> Result<D, D::Error>
が提供されている。
実装ではDeserializer
を作成して、Deserialize::deserialize(deserializer)
を呼べばよい。
pub fn from_node<'de, D: serde::Deserialize<'de>>(
node: tree_sitter::Node<'de>,
src: &'de str,
) -> Result<D, DeserializeError> {
let deserializer = NodeDeserializer::new(TsNodeImpl { node, src });
D::deserialize(deserializer)
}
参考資料
- serdeのdocs.rs
serde::Deserializer
serde::Deserialize
serde::de::Visitor
serde::de::Error
-
serde::de::*Access
trait
- serde公式ページ
- serdeマニュアル -Deserializerについて- | 株式会社RICOS
- SerdeのDeserializerを実装する(Part2 JSON編)
Discussion