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)とか書かなくてはいけなくなって大変。
defaultをtrueにする
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