Serialized defaults in serde

Is it possible to deserialize it in such a way that if a value is not provided for an entry, it would look up the value from a different "default" entry?

For example, suppose I have a struct like this:

#[derive(Deserialize, Serialize)]
struct Entry {
    reticulate_splines: bool,
    integrate_curves: bool,
    align_covariance_matrices: bool,
    chlorinate_threadpool: bool,
}

#[derive(Deserialize, Serialize)]
struct Config {
    defaults: Entry,

    #[serde(rename = "entry")]
    entries: HashMap<String, Entry>,
}

I want to deserialize a config like the one below, where it would load the values defined in entries, and if missing, look up in "defaults", and fail if it's not in either.

[defaults]
integrate_curves = false
align_covariance_matrices = false
chlorinate_threadpool = true

[entry.Foo]
reticulate_splines = true

[entry.Bar]
reticulate_splines = false
chlorinate_threadpool = false

Is it possible? How would I go about doing it?

One way is to make all fields optional and manually merge after deserializing, but that's annoying.

I'm guessing it should be possible to implement a custom Deserialize for Config but I'm a bit lost on how to do it.

Not sure how you would do if from a config file but this is how to do it on the struct Default value for a field · Serde

#[derive(Deserialize, Serialize)]
struct Entry {
#[serde(default = true)]
reticulate_splines: bool,
#[serde(default = false)]
integrate_curves: bool,
#[serde(default = false)]
align_covariance_matrices: bool,
#[serde(default = false)]
chlorinate_threadpool: bool,
}

You might be able to create something like that with DeserializeSeed (or maybe serde_state).
If it is for a configuration system, maybe a specialized crate which has a concept of layers which get merged might be more useful. A quick search revealed config and twelf.

1 Like

I use a strategy to use an intermediary struct just for deserailizing then convert that into a struct for coding.

warning: i'm sure this code doesn't compile, but i hope it gives you an idea to my approach.

#[derive(Deserialize, Serialize)]
struct EntryOption {
    reticulate_splines: Option<bool>,
    integrate_curves: Option<bool>,
    align_covariance_matrices: Option<bool>,
    chlorinate_threadpool: Option<bool>,
}

#[derive(Deserialize, Serialize)]
struct Entry {
    reticulate_splines: bool,
    integrate_curves: bool,
    align_covariance_matrices: bool,
    chlorinate_threadpool: bool,
}

#[derive(Deserialize, Serialize)]
struct ConfigOption {
    defaults: Entry,

    #[serde(rename = "entry")]
    entries: HashMap<String, EntryOption>,
}
impl ConfigOption {
  fn into_config(Self) -> Config {
     let defaults_entry: Self.defaults;
     let entries = Self.enteries.map( |(k,v)| {
        let new_entry = Entry {
             reticulate_splines = v.reticulate_splines.unwrap_or(defaults_entry.reticulate_splines)
             integrate_curves = v.integrate_curves.unwrap_or(defaults_entry.integrate_curves)
             align_covariance_matrices = v.align_covariance_matrices.unwrap_or(defaults_entry.align_covariance_matrices)
             chlorinate_threadpool = v.chlorinate_threadpool.unwrap_or(defaults_entry.chlorinate_threadpool)
         }
         (k,new_entry)
      }).collect::<Vec<_>>()
      Config {
         defaults: defaults_entry,
         entries: entries
      }
  }
}

struct Config {
    defaults: Entry,
    entries: HashMap<String, Entry>,
}
1 Like

Thanks, DeserializeSeed looks like the building block I'm looking for.

What I'm trying to do is not so much to merge configs, as to de-duplicate repetitive entries. Basically, a #[serde(default = ...)] where I don't hard-code the default. My config can have a large number of user-defined entries, and I want to have a global section that can be used to set the defaults that would apply to each entry that doesn't have those value explicitly set.

Thanks, this does work, but I'm hoping to have something a bit more generic.

Here's a version that compiles:

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.