Rust + serdeでJSONのデシリアライズ
2021-09-23
- プログラミング
Rustの練習でツールを作っている。その中で、APIレスポンスのJSONを構造体にデシリアライズするときに悩んだのでメモ。
主に悩んだのは、デシリアライズ先の構造体のフィールドが Vec<構造体>
とか Option<構造体>
のとき。特に Option<構造体>
にデシリアライズしたいデータがJSONに存在しないときにNoneを割り当てるにはどうすればいいかという点。
この記事のコードはここにある。
デシリアライズのサンプルを person1
から person6
まで作っているが、 person3
は一部動かないので、この記事では書かない。
自分がやりたかったこととしては person2
で完了している。 person5
や person6
の方法でもいいかもという感じ。
この記事ではシリアライズは一切考えない。
やりたいこと
以下のようなJSONがある。イメージとしては、なにかのサークルのメンバーリストで、年齢や会社名などが記録されたもの。会社名(company
)は所属がない場合は null
になったり、フィールド自体がなかったりするものとする。
{
"name": "Alice",
"age": 42,
"is_active": true,
"mails": [
"alice@example.com",
"wonderland@example.com"
],
"company": "ABC technologies"
}
これを以下のような構造体にデシリアライズしたい。 Name
や Age
などはフィールド1つだけを持っている構造体。
struct Person {
name: Name,
age: Age,
is_active: IsActive,
mails: Vec<MailAddress>,
company: Option<Company>,
}
いくつか例を書く。
- 普通のもの(JSON1)
company
に値が入っている。このときはSome(...)
にしたい。{ "name": "Alice", "age": 42, "is_active": true, "mails": [ "alice@example.com", "wonderland@example.com" ], "company": "ABC technologies" }
- デシリアライズ後
Person { name: Name { value: "Alice".to_string(), }, age: Age { value: 42 }, is_active: IsActive { value: true }, mails: vec![ MailAddress { value: "alice@example.com".to_string(), }, MailAddress { value: "wonderland@example.com".to_string(), }, ], company: Some(Company { value: "ABC technologies".to_string(), }), }
- デシリアライズ後
company
がnullのもの(JSON2)
このときはNone
として扱いたい。{ "name": "Bob", "age": 43, "is_active": false, "mails": [ "bob@example.com" ], "company": null }
- デシリアライズ後
Person { name: Name { value: "Bob".to_string(), }, age: Age { value: 43 }, is_active: IsActive { value: false }, mails: vec![MailAddress { value: "bob@example.com".to_string(), }], company: None, }
- デシリアライズ後
company
フィールド自体ないもの(JSON3)
この場合もNone
として扱いたい。{ "name": "Carol", "age": 44, "is_active": true, "mails": [] }
- デシリアライズ後
Person { name: Name { value: "Carol".to_string(), }, age: Age { value: 44 }, is_active: IsActive { value: true }, mails: vec![], company: None, }
- デシリアライズ後
環境
- Rust
1.54.0 - serde
1.0 - serde_json
1.0 - serde_with
1.10.0
Cargo.toml
この記事のコードは [dependencies]
以降をこのようにした状態で動かす。
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
[dependencies.serde_with]
version = "1.10.0"
serde
と serde_json
は全体で使う。
serde_with
は最後の方で使うだけ(person6)。
単純な例(person1)
まず、 Person
構造体のフィールドが構造体ではなく、単なる文字列型などの場合を考えてみる。
基本的な文字列型や整数型などは #[derive(Deserialize)]
と書いておけば、serde_jsonでよろしくやってくれる。OptionやVecも同様。例がドキュメントにある。
これを使うと以下のようになる。
なおderiveで Debug
と書いてあるのはprintlnしたいため、 PartialEq
や Eq
はテストでassert_eqしたいためなので、これらはデシリアライズとは関係ない。
person1.rs
use serde::Deserialize;
#[derive(Deserialize, Debug, PartialEq, Eq)]
pub struct Person {
name: String,
age: u8,
is_active: bool,
mails: Vec<String>,
company: Option<String>,
}
pub fn json_to_person(json: &str) -> Result<Person, serde_json::Error> {
serde_json::from_str(json)
}
main.rs
use sample_deserialize_with_serde::jsons::{JSON1, JSON2, JSON3};
use sample_deserialize_with_serde::person1;
fn main() {
// person1
println!("[person1]");
let p1_1: person1::Person = person1::json_to_person(JSON1).unwrap();
let p1_2: person1::Person = person1::json_to_person(JSON2).unwrap();
let p1_3: person1::Person = person1::json_to_person(JSON3).unwrap();
println!("p1_1 = {:?}", p1_1);
println!("p1_2 = {:?}", p1_2);
println!("p1_3 = {:?}", p1_3);
}
cargo run
の結果
[person1]
p1_1 = Person { name: "Alice", age: 42, is_active: true, mails: ["alice@example.com", "wonderland@example.com"], company: Some("ABC technologies") }
p1_2 = Person { name: "Bob", age: 43, is_active: false, mails: ["bob@example.com"], company: None }
p1_3 = Person { name: "Carol", age: 44, is_active: true, mails: [], company: None }
カスタムな型にマッピングする(person2)
上の例ではJSONのフィールドの値をStringやu8など単純な型にした。
こうするよりもカスタムな型にマッピングする方があとあと扱いやすいことがある(特にDDDでやっているとき)。
たとえば何かの処理で name(String型)
のVecを作る場合に、誤って mails(Vec<String>型)
の要素が紛れ込んでしまったとする。このとき、どちらもStringなのでミスに気付けない。違う型にすればコンパイルエラーになるのですぐ気付くことができる。
そこで
struct Person {
name: String,
age: u8,
is_active: bool,
mails: Vec<String>,
company: Option<String>,
}
としていたのを
struct Person {
name: Name,
age: Age,
is_active: IsActive,
mails: Vec<MailAddress>,
company: Option<Company>,
}
にする。つまりフィールドを構造体にした。
(たぶん実際にはここまでカスタムな型にする必要はないはず。たとえば is_active
はboolのままで何も困らないと思う。他のフィールドも場合によりけり。今回は練習のためにこうやっている。)
さて Name
などは適当な構造体にするが、JSONのどのフィールドを、構造体のどのフィールドにどうマッピングするかは自分で書く必要がある。
これは Name
を次のように定義したときを考えればわかる。
struct Name {
first_name: String,
last_name: String,
}
この場合、JSONの "name": "Alice"
の情報を Name
のどのフィールドに入れるのかは、Rust側ではわからない。
今回はこう定義せずに
struct Name {
value: String,
}
とするが、このようにフィールドを1つだけにしても、自分でデシリアライズ処理を書かないといけないっぽい。
このようにデシリアライザを自分で書くときは以下を参考にする。
- Implementing Deserialize · Serde
serdeでカスタムなデシリアライザを書いてJSONをデシリアライズする方法。 - serde::de::Visitor - Rust
Visitorトレイトのドキュメント。visit_...
系メソッド一覧がある。
person2.rs
フィールドの構造体一つ一つに対し、 Visitor
と Deserialize
を実装する。
Age.value
の型はu8なのに visit_u64
しか実装していないのが変に見えるが、これは visit_u8
のデフォルト実装が visit_u64
だかららしい。 visit_u8
だけを実装すると実行時エラーになる。
これはvisit_u8のドキュメントに書いてある。
Vec<MailAddress>
や Option<Company>
については、MailAddressやCompanyに対する処理を書けばいいだけで、他に特別な処理を書く必要はないようだ。これは、MailAddressやCompany自体は構造体だが、それがVecやOptionにくるまれているので、serdeの機能で自動的に対処してくれるということなのだろう(…たぶん)。
use serde::Deserialize;
use serde::de::{self, Visitor};
use std::fmt;
#[derive(Deserialize, Debug, PartialEq, Eq)]
pub struct Person {
name: Name,
age: Age,
is_active: IsActive,
mails: Vec<MailAddress>,
company: Option<Company>,
}
#[derive(Debug, PartialEq, Eq)]
struct Name {
value: String,
}
#[derive(Debug, PartialEq, Eq)]
struct Age {
value: u8,
}
#[derive(Debug, PartialEq, Eq)]
struct IsActive {
value: bool,
}
#[derive(Debug, PartialEq, Eq)]
struct MailAddress {
value: String,
}
#[derive(Debug, PartialEq, Eq)]
struct Company {
value: String,
}
struct NameVisitor;
impl<'de> Visitor<'de> for NameVisitor {
type Value = Name;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("String for Name")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Name {
value: v.to_string(),
})
}
}
impl<'de> Deserialize<'de> for Name {
fn deserialize<D>(deserializer: D) -> Result<Name, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_str(NameVisitor)
}
}
struct AgeVisitor;
impl<'de> Visitor<'de> for AgeVisitor {
type Value = Age;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("Integer for Age")
}
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Age { value: v as u8 })
}
}
impl<'de> Deserialize<'de> for Age {
fn deserialize<D>(deserializer: D) -> Result<Age, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_u64(AgeVisitor)
}
}
struct IsActiveVisitor;
impl<'de> Visitor<'de> for IsActiveVisitor {
type Value = IsActive;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("Bool for IsActive")
}
fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(IsActive { value: v })
}
}
impl<'de> Deserialize<'de> for IsActive {
fn deserialize<D>(deserializer: D) -> Result<IsActive, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_bool(IsActiveVisitor)
}
}
struct MailAddressVisitor;
impl<'de> Visitor<'de> for MailAddressVisitor {
type Value = MailAddress;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("String for MailAddress")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(MailAddress {
value: v.to_string(),
})
}
}
impl<'de> Deserialize<'de> for MailAddress {
fn deserialize<D>(deserializer: D) -> Result<MailAddress, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_str(MailAddressVisitor)
}
}
struct CompanyVisitor;
impl<'de> Visitor<'de> for CompanyVisitor {
type Value = Company;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("String for Company")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Company {
value: v.to_string(),
})
}
}
impl<'de> Deserialize<'de> for Company {
fn deserialize<D>(deserializer: D) -> Result<Company, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_str(CompanyVisitor)
}
}
pub fn json_to_person(json: &str) -> Result<Person, serde_json::Error> {
serde_json::from_str(json)
}
main.rs
use sample_deserialize_with_serde::jsons::{JSON1, JSON2, JSON3};
use sample_deserialize_with_serde::person2;
fn main() {
// person2
println!("\n[person2]");
let p2_1: person2::Person = person2::json_to_person(JSON1).unwrap();
let p2_2: person2::Person = person2::json_to_person(JSON2).unwrap();
let p2_3: person2::Person = person2::json_to_person(JSON3).unwrap();
println!("p2_1 = {:?}", p2_1);
println!("p2_2 = {:?}", p2_2);
println!("p2_3 = {:?}", p2_3);
}
cargo run
の結果
[person2]
p2_1 = Person { name: Name { value: "Alice" }, age: Age { value: 42 }, is_active: IsActive { value: true }, mails: [MailAddress { value: "alice@example.com" }, MailAddress { value: "wonderland@example.com" }], company: Some(Company { value: "ABC technologies" }) }
p2_2 = Person { name: Name { value: "Bob" }, age: Age { value: 43 }, is_active: IsActive { value: false }, mails: [MailAddress { value: "bob@example.com" }], company: None }
p2_3 = Person { name: Name { value: "Carol" }, age: Age { value: 44 }, is_active: IsActive { value: true }, mails: [], company: None }
タプル構造体を使う(person4)
上(person2)で、「JSONのどのフィールドを、構造体のどのフィールドにマッピングするかはRust側にはわからないので、自分で処理を書く必要がある」のように書いた。
要素数1つのタプル構造体ならRust側にもどうマッピングすればいいかわかるのではと思ったので、やってみたら一応うまくいったっぽい。今回のように単純な構造体にマッピングするならこれでいいと思う。
person4.rs
use serde::Deserialize;
#[derive(Deserialize, Debug, PartialEq, Eq)]
pub struct Person {
name: NameTuple,
age: AgeTuple,
is_active: IsActiveTuple,
mails: Vec<MailAddressTuple>,
company: Option<CompanyTuple>,
}
#[derive(Deserialize, Debug, PartialEq, Eq)]
struct NameTuple(String);
#[derive(Deserialize, Debug, PartialEq, Eq)]
struct AgeTuple(u8);
#[derive(Deserialize, Debug, PartialEq, Eq)]
struct IsActiveTuple(bool);
#[derive(Deserialize, Debug, PartialEq, Eq)]
struct MailAddressTuple(String);
#[derive(Deserialize, Debug, PartialEq, Eq)]
struct CompanyTuple(String);
pub fn json_to_person(json: &str) -> Result<Person, serde_json::Error> {
serde_json::from_str(json)
}
main.rs
use sample_deserialize_with_serde::jsons::{JSON1, JSON2, JSON3};
use sample_deserialize_with_serde::person4;
fn main() {
// person4
println!("\n[person4]");
let p4_1: person4::Person = person4::json_to_person(JSON1).unwrap();
let p4_2: person4::Person = person4::json_to_person(JSON2).unwrap();
let p4_3: person4::Person = person4::json_to_person(JSON3).unwrap();
println!("p4_1 = {:?}", p4_1);
println!("p4_2 = {:?}", p4_2);
println!("p4_3 = {:?}", p4_3);
}
cargo run
の結果
[person4]
p4_1 = Person { name: NameTuple("Alice"), age: AgeTuple(42), is_active: IsActiveTuple(true), mails: [MailAddressTuple("alice@example.com"), MailAddressTuple("wonderland@example.com")], company: Some(CompanyTuple("ABC technologies")) }
p4_2 = Person { name: NameTuple("Bob"), age: AgeTuple(43), is_active: IsActiveTuple(false), mails: [MailAddressTuple("bob@example.com")], company: None }
p4_3 = Person { name: NameTuple("Carol"), age: AgeTuple(44), is_active: IsActiveTuple(true), mails: [], company: None }
deserialize_with
アトリビュートを使う(person5)
今回、serdeを使ったJSONデシリアライズの例を調べていたら、このアトリビュートを使っている人が多かった。こっちの方が一般的なのだろうか…?
person2の例だと Visitor
と Deserialize
を自分で実装しなければならず、記述量が多かった。
deserialize_with
を使うとちょっと少なくできる。ただし Option<T>
や Vec<T>
などは工夫する必要があるようだ。
person5.rs
deserialize_with
の使い方としては #[serde(deserialize_with="デシリアライズに使う関数の名前")]
をフィールドにつけて、そのデシリアライズ用関数を書く。
Vec<MailAddress>
については、 deserialize_vec_mail_address
でVecを外し、1つ1つの要素については deserialize_mail_address
を使ってデシリアライズするという方法を取っている。 Option<Company>
も同様。
これは以下を参考にした(つもりだが、ちゃんと理解できたかわからない)。
- Option
を serde(with="...")するイディオム - Qiita - Using de/serialize_with inside of an Option, Map, Vec · Issue #723 · serde-rs/serde · GitHub
あとこのアトリビュートを使うと、JSONに company
フィールドがないときに実行時エラーが起きるようだった。そのため #[serde(default = "default_option_company")]
というアトリビュートもつけ、 default_option_company()
という関数でNoneを返している。
use serde::de::Deserializer;
use serde::Deserialize;
#[derive(Deserialize, Debug, PartialEq, Eq)]
pub struct Person {
#[serde(deserialize_with = "deserialize_name")]
name: Name,
#[serde(deserialize_with = "deserialize_age")]
age: Age,
#[serde(deserialize_with = "deserialize_is_active")]
is_active: IsActive,
#[serde(deserialize_with = "deserialize_vec_mail_address")]
mails: Vec<MailAddress>,
#[serde(deserialize_with = "deserialize_option_company")]
#[serde(default = "default_option_company")]
company: Option<Company>,
}
#[derive(Debug, PartialEq, Eq)]
struct Name {
value: String,
}
#[derive(Debug, PartialEq, Eq)]
struct Age {
value: u8,
}
#[derive(Debug, PartialEq, Eq)]
struct IsActive {
value: bool,
}
#[derive(Debug, PartialEq, Eq)]
struct MailAddress {
value: String,
}
#[derive(Debug, PartialEq, Eq)]
struct Company {
value: String,
}
fn deserialize_name<'de, D>(deserializer: D) -> Result<Name, D::Error>
where
D: Deserializer<'de>,
{
let v = String::deserialize(deserializer)?;
Ok(Name { value: v })
}
fn deserialize_age<'de, D>(deserializer: D) -> Result<Age, D::Error>
where
D: Deserializer<'de>,
{
let v = u8::deserialize(deserializer)?;
Ok(Age { value: v })
}
fn deserialize_is_active<'de, D>(deserializer: D) -> Result<IsActive, D::Error>
where
D: Deserializer<'de>,
{
let v = bool::deserialize(deserializer)?;
Ok(IsActive { value: v })
}
fn deserialize_mail_address<'de, D>(deserializer: D) -> Result<MailAddress, D::Error>
where
D: Deserializer<'de>,
{
let v = String::deserialize(deserializer)?;
Ok(MailAddress { value: v })
}
fn deserialize_vec_mail_address<'de, D>(deserializer: D) -> Result<Vec<MailAddress>, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct Wrapper(#[serde(deserialize_with = "deserialize_mail_address")] MailAddress);
let v = Vec::deserialize(deserializer)?;
Ok(v.into_iter().map(|Wrapper(a)| a).collect())
}
fn deserialize_company<'de, D>(deserializer: D) -> Result<Company, D::Error>
where
D: Deserializer<'de>,
{
let v = String::deserialize(deserializer)?;
Ok(Company { value: v })
}
fn deserialize_option_company<'de, D>(deserializer: D) -> Result<Option<Company>, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct Wrapper(#[serde(deserialize_with = "deserialize_company")] Company);
let v = Option::deserialize(deserializer)?;
Ok(v.map(|Wrapper(a)| a))
}
fn default_option_company() -> Option<Company> {
None
}
pub fn json_to_person(json: &str) -> Result<Person, serde_json::Error> {
serde_json::from_str(json)
}
main.rs
use sample_deserialize_with_serde::jsons::{JSON1, JSON2, JSON3};
use sample_deserialize_with_serde::person5;
fn main() {
// person5
println!("\n[person5]");
let p5_1: person5::Person = person5::json_to_person(JSON1).unwrap();
let p5_2: person5::Person = person5::json_to_person(JSON2).unwrap();
let p5_3: person5::Person = person5::json_to_person(JSON3).unwrap();
println!("p5_1 = {:?}", p5_1);
println!("p5_2 = {:?}", p5_2);
println!("p5_3 = {:?}", p5_3);
}
cargo run
の結果
person2
のときと同じ。
[person5]
p5_1 = Person { name: Name { value: "Alice" }, age: Age { value: 42 }, is_active: IsActive { value: true }, mails: [MailAddress { value: "alice@example.com" }, MailAddress { value: "wonderland@example.com" }], company: Some(Company { value: "ABC technologies" }) }
p5_2 = Person { name: Name { value: "Bob" }, age: Age { value: 43 }, is_active: IsActive { value: false }, mails: [MailAddress { value: "bob@example.com" }], company: None }
p5_3 = Person { name: Name { value: "Carol" }, age: Age { value: 44 }, is_active: IsActive { value: true }, mails: [], company: None }
serde_withを使う(person6)
person5では Vec<T>
や Option<T>
の処理が面倒だった。serde_withを使うと少し楽になる。
person6.rs
mails
と company
で serde_as
というのを使っている。これがserde_withのアトリビュート。
これをつけておけば、デシリアライズ先の構造体(いまの場合は MailAddress
や Company
)がFromStrを実装してあればそれを使ってデシリアライズしてくれる、ということらしい。なのでFromStrを実装する必要はある。
なお以下の例では Name
でもdeserialize_asを使っている。これは単なる練習であり、person5のときのようにdeserialize_withを使っても変わらない。できれば Age
や IsActive
でも使ってみたかったが、方法がよくわからなかった。
use serde::de::Deserializer;
use serde::Deserialize;
use serde_with::{serde_as, DisplayFromStr};
use std::str::FromStr;
use std::string::ParseError;
#[serde_as]
#[derive(Deserialize, Debug, PartialEq, Eq)]
pub struct Person {
#[serde_as(deserialize_as = "DisplayFromStr")]
name: Name,
#[serde(deserialize_with = "deserialize_age")]
age: Age,
#[serde(deserialize_with = "deserialize_is_active")]
is_active: IsActive,
#[serde_as(deserialize_as = "Vec<DisplayFromStr>")]
mails: Vec<MailAddress>,
#[serde_as(deserialize_as = "Option<DisplayFromStr>")]
#[serde(default = "default_option_company")]
company: Option<Company>,
}
#[derive(Debug, PartialEq, Eq)]
struct Name {
value: String,
}
impl FromStr for Name {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Name {
value: s.to_string(),
})
}
}
#[derive(Debug, PartialEq, Eq)]
struct Age {
value: u8,
}
#[derive(Debug, PartialEq, Eq)]
struct IsActive {
value: bool,
}
#[derive(Debug, PartialEq, Eq)]
struct MailAddress {
value: String,
}
#[derive(Debug, PartialEq, Eq)]
struct Company {
value: String,
}
fn deserialize_age<'de, D>(deserializer: D) -> Result<Age, D::Error>
where
D: Deserializer<'de>,
{
let v = u8::deserialize(deserializer)?;
Ok(Age { value: v })
}
fn deserialize_is_active<'de, D>(deserializer: D) -> Result<IsActive, D::Error>
where
D: Deserializer<'de>,
{
let v = bool::deserialize(deserializer)?;
Ok(IsActive { value: v })
}
fn default_option_company() -> Option<Company> {
None
}
impl FromStr for MailAddress {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(MailAddress {
value: s.to_string(),
})
}
}
impl FromStr for Company {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Company {
value: s.to_string(),
})
}
}
pub fn json_to_person(json: &str) -> Result<Person, serde_json::Error> {
serde_json::from_str(json)
}
main.rs
use sample_deserialize_with_serde::jsons::{JSON1, JSON2, JSON3};
use sample_deserialize_with_serde::person6;
fn main() {
// person6
println!("\n[person6]");
let p6_1: person6::Person = person6::json_to_person(JSON1).unwrap();
let p6_2: person6::Person = person6::json_to_person(JSON2).unwrap();
let p6_3: person6::Person = person6::json_to_person(JSON3).unwrap();
println!("p6_1 = {:?}", p6_1);
println!("p6_2 = {:?}", p6_2);
println!("p6_3 = {:?}", p6_3);
}
cargo run
の結果
person2のときと同じ。
[person6]
p6_1 = Person { name: Name { value: "Alice" }, age: Age { value: 42 }, is_active: IsActive { value: true }, mails: [MailAddress { value: "alice@example.com" }, MailAddress { value: "wonderland@example.com" }], company: Some(Company { value: "ABC technologies" }) }
p6_2 = Person { name: Name { value: "Bob" }, age: Age { value: 43 }, is_active: IsActive { value: false }, mails: [MailAddress { value: "bob@example.com" }], company: None }
p6_3 = Person { name: Name { value: "Carol" }, age: Age { value: 44 }, is_active: IsActive { value: true }, mails: [], company: None }
おわりに
むずい。書き方はこれでいいのだと思うが、なぜそう書けるのかがわかってない、ということがこれを書いていてよくわかった。デシリアライズするときにserde内部でどんな処理が行われているのか、調べる必要がある。
参考
本文中に挙げたもの以外を書く。
- Deserializer lifetimes · Serde
lifetimeについて。よくわかってない…。 - serde::de - Rust
serdeが(自分でシリアライズ/デシリアライズ処理を書かなくても)自動で変換してくれる型が書いてある。 - Attributes · Serde
serdeのアトリビュートのドキュメント。 - serdeのかゆいところに手が届くserde_with - Qiita
serde_withの使い方とか。
変更履歴
- 2021/09/29
文章表現の一部を修正。