Search code examples
rustserdetoml

How to serialize/deserialize a map with the None values


I need a map with the Option values in my configuration. However, serde seems to ignore any pairs with the None value

use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use toml;

#[derive(Debug, Serialize, Deserialize)]
struct Config {
    values: HashMap<String, Option<u32>>,
}

fn main() {
    let values = [("foo", Some(5)), ("bar", None)]
        .iter()
        .map(|(name, s)| (name.to_string(), s.clone()))
        .collect();
    let config = Config { values };

    let s = toml::ser::to_string(&config).unwrap();
    println!("{}", s);
}

produces

[values]
foo = 5

The same goes for deserializing: I simply cannot represent bar: None in any form, since the TOML has no notion of None or null or alike.

Are there some tricks to do that?


Solution

  • The closest alternative I have found is to use a special sentinel value (the one you will probably use in Option::unwrap_or), which appears in the TOML file as the real value (e.g. 0), and converts from Option::None on serialization. But on deserialization, the sentinel value converts to Option::None and leaves us with the real Option type.

    Serde has a special #[serde(with = module)] attribute to customize the ser/de field behavior, which you can use here. The full working example is here.