I'm writing a config file handler for an application where I have a config struct with default values through a default implementation. The configs are written to disk in JSON, but I only want to write values that are different from the defaults to disk. Is there a way to extract only the different values and write them out as JSON? I found serde_diff but it doesn't appear to have a way to do this directly.
#Diffing a struct and its default values to JSON
12 messages · Page 1 of 1 (latest)
#[serde(skip_serializing_if = "ne_default")] on the field, with ```rs
fn ne_default<T: PartialEq + Default>(val: &T) -> bool {
val != T::default()
}
assuming the default value of the field is given by the trait Default
otherwise use a different function
Is there a way to skip serialization only when performing a certain function? I want to serialize the fields normally, just not when writing to disk
You could have a newtype wrapper that implements Serialize, something like: ```rs
struct MyType {
a: String,
b: u32,
c: f64,
}
impl MyType {
fn serialize(&self, skip_defaults: bool) -> Serialize<'_> {
Serialize {
inner: self,
skip_defaults,
}
}
}
struct Serialize<'inner> {
inner: &'inner MyType,
skip_defaults: bool,
}
impl serde::Serialize for Serialize<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let serializer = serializer.serialize_struct("MyType", 3)?;
if !(self.skip_defaults && self.inner.a == "default_string") {
serializer.serialize_field("a", self.inner.a)?;
}
if !(self.skip_defaults && self.inner.b == 0) {
serializer.serialize_field("b", self.inner.b)?;
}
serializer.serialize_field("c", self.inner.c)?;
serializer.end()
}
}
might get boilerplately though
Thanks, so the defaults are declared in the serialize function? This would need to be reimplemented for each struct that makes up the config, yes?
Yeah, so that’d the boilerplatey disadvantage
Wait, it could be made nicer with some traits
pub trait SerializeConfig<Config> {
fn serialize_config<S: Serializer>(&self, config: &Config) -> Result<S::Ok, S::Error>;
fn serialize_with<'this, 'config>(&'this self, config: &'config Config) -> SerializeWith<'this, 'config, Self, Config>;
}
pub struct SerializeWith<'this, 'config, T, Config> {
this: &'this T,
config: &'config Config,
}
// impl Serialize for SerializeWith
^ the use of that trait will reduce boilerplate a lot