Implement serialize to skip field key name and new line if other optional field is None

42 Views Asked by At

I have the following structs:

#[derive(Debug, Serialize)]
struct Container {
    field0: Field<f64>,
    field1: Field<f64>,
}

#[derive(Debug, Serialize)]
struct Field<T> {
    val: T,
    #[serde(skip_serializing_if = "Option::is_none")]
    doc: Option<String>,
}

For this instance:

Container {
    field0: Field { val: 1.5, doc: Some("my documentation!") },
    field1: Field { val: 2.0, doc: None }
}

I want the following YAML output (since doc is None):

field0:
  val: 1.5
  doc: my documentation!
field1: 2.0

Of course, the derived setup results in this:

field0:
  val: 1.5
  doc: my documentation!
field1:
  val: 2.0

where val is serialized on a new line

I tried implementing Serialize for Field myself, but wasn't quite sure how to get the key name to go away, and how to get the value on the same line as the name

impl<T> Serialize for Field<T> where T: Serialize {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        if self.doc.is_some() {
            let mut state = serializer.serialize_struct("Field", 2)?;
            state.serialize_field("val", &self.val)?;
            state.serialize_field("doc", &self.doc)?;
            state.end()
        } else {
            let mut state = serializer.serialize_struct("Field", 1)?;
            state.serialize_field("", &self.val)?;
            state.skip_field("doc")?;
            state.end()
        }
    }
}

resulting in the output:

field0:
  val: 1.5
  doc: my documentation!
field1:
  '': 2.0

How should I go about implementing it? Is there an easier/cleaner way?

RUST PLAYGROUND LINK

1

There are 1 best solutions below

4
kmdreko On BEST ANSWER

Easy fix. If doc is None, then you don't want to serialize a struct, just serialize the value directly:

if self.doc.is_some() {
    let mut state = serializer.serialize_struct("Field", 2)?;
    state.serialize_field("val", &self.val)?;
    state.serialize_field("doc", &self.doc)?;
    state.end()
} else {
    self.val.serialize(serializer)
}

With your playground code produces:

field0:
  val: 1.5
  doc: my documentation!
field1: 2.0