Serdeでfalseのフィールドを無視する
Using derive - Serdeのサンプルコードにname
というOption<String>
のフィールドを追加した次のコードからはじめましょう。
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
struct Point {
x: i32,
y: i32,
name: Option<String>,
}
fn main() {
let point = Point { x: 1, y: 2, name: None };
let serialized = serde_json::to_string(&point).unwrap();
println!("serialized = {}", serialized);
let deserialized: Point = serde_json::from_str(&serialized).unwrap();
println!("deserialized = {:?}", deserialized);
}
このコードを実行すると
> cargo run
...
serialized = {"x":0,"y":0,"name":null}
deserialized = Point { x: 0, y: 0, name: None }
name
はNone
なので保存したくないですよね。
Serdeではskip_serialize_if
を使うと、特定の条件のときにフィールドを無視することができます。
#[serde(skip_serializing_if = "Option::is_none")]
name: Option<String>,
こうするとname: null
が出力されなくなりました。
> cargo run
...
serialized = {"x":1,"y":2}
deserialized = Point { x: 1, y: 2, name: None }
さらに、bool
のフィールドを追加します。
#[derive(Serialize, Deserialize, Debug)]
struct Point {
x: i32,
y: i32,
#[serde(skip_serializing_if = "Option::is_none")]
name: Option<String>,
hidden: bool,
}
実行してみましょう。
...
serialized = {"x":1,"y":2,"hidden":false}
deserialized = Point { x: 1, y: 2, name: None, hidden: false }
やはり、false
は無視したいですね。skip_serializing_if
には関数を指定する必要がありますので、std::ops::Not::notを使えばfalse
のときtrue
になってskipされそうです。
use std::ops::Not;
...
#[serde(skip_serializing_if = "Not::not")]
hidden: bool,
実行するとserializeはうまくいきましたが、deserializeでエラーになります。 [1]
> cargo run
...
serialized = {"x":1,"y":2}
thread 'main' panicked at src\main.rs:23:65:
called `Result::unwrap()` on an `Err` value: Error("missing field `hidden`", line: 1, column: 13)
これを解決するためにはdefaultを設定する必要があります。
#[serde(default, skip_serializing_if = "Not::not")]
hidden: bool,
このserde(default)
はPoint
がDefault
をderiveしていても関係なく必要です。
defaultはどっちのdefault?
Point
のDefault
でhidden
がtrue
になっているとき、serde(default)
はtrue
/ false
のどちらだろう? Point
がDefault
を実装しているかは無関係なことからbool
のdefaultのfalse
になるんじゃないかなと予想するけど
#[derive(Serialize, Deserialize, Debug)]
struct Point {
x: i32,
y: i32,
#[serde(skip_serializing_if = "Option::is_none")]
name: Option<String>,
#[serde(default, skip_serializing_if = "Not::not")]
hidden: bool,
}
impl Default for Point {
fn default() -> Self {
Point { x: 0, y: 0, name: None, hidden: true }
}
}
実行すると、
> cargo run
...
serialized = {"x":1,"y":2}
deserialized = Point { x: 1, y: 2, name: None, hidden: false }
予想通りですね。
Donts
bool
ではなくOption<bool>
ぼくも上の書き方を知るまではそうしていました。でも、これだとNone
, Some(false)
, Some(true)
の3つ値を考えないといけなくなるんですよね。
使うときにpoint.hidden.unwrap_or(false)
とか書かなくてはいけなくなって大変。
true
にする
defaultをbool
のフィールドのデフォルトをtrue
にするというのは技術的にはできますが、お勧めしません。
プログラムのバージョンアップとともに、フィールドは追加されたり削除されたりしていきます。しかし、一度、serializeされたデータはどこかに残っているものです。未来のプログラムが問題なく動作するためには、フィールドの型のデフォルト値を使うのがもっとも自然で安全です。
例えばhidden
のデフォルトをtrue
にしたいのであれば、フィールド名を反対の意味のvisible
にすればデフォルトはfalse
になります。[2]
最終的なコード
Cargo.toml
[package]
name = "serde_skip_false"
version = "0.1.0"
edition = "2024"
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
src/main.rs
use std::ops::Not;
use serde::{Serialize, Deserialize};
#[derive(Default, Serialize, Deserialize, Debug)]
struct Point {
x: i32,
y: i32,
#[serde(skip_serializing_if = "Option::is_none")]
name: Option<String>,
#[serde(default, skip_serializing_if = "Not::not")]
hidden: bool,
}
fn main() {
let point = Point { x: 1, y: 2, name: None, hidden: false };
// let point = Point::default();
let serialized = serde_json::to_string(&point).unwrap();
println!("serialized = {}", serialized);
let deserialized: Point = serde_json::from_str(&serialized).unwrap();
println!("deserialized = {:?}", deserialized);
}
Discussion