How to serialize an option with serde?
Asked Answered
H

3

6

I have created a serialize_foo function that can serialize the Foo struct.

struct Foo(i32) <- The Foo struct looks like this.

The Foo struct is used in another struct Bar that looks like this:

struct Bar {
  #[serde(serialize_with = "serialize_foo")]
  pub x: Foo,
  #[serde(serialize_with = "serialize_foo")]
  pub y: Option<Foo>
}

The x field can be serialized with the serialize_foo function, but the y field can't. It is because it is an Option.

How should I serialize an Option, should there be a new function serialize_foo_option, but what would the function do if the value is None.

Hennie answered 2/10, 2022 at 5:57 Comment(7)
Do you control the Foo struct? If so, you should make serialize_foo be the actual Serialize implementation for Foo, and then you'll get all of the composed types like Option<Foo>, Vec<Foo>, etc. for free.Drennan
@SilvioMayolo No, I am not able to make the actual implementation of Foo.Hennie
As suggested in this thread, you could wrap the Foo struct in a wrapper type as shown in this playground (always from the former thread)Spirochete
You could make an alternate function that does return an option, like try_serialize_foo. That really just contains a map call on the Option.Padilla
You can use docs.rs/serde_with/2/serde_with/guide/serde_as/index.html to make it work with any nesting like Vec<Foo> or BTreeMap<Foo, i32>. The link explains how to use the existing serialize_foo function for that.Borek
@Padilla I think this is the best solution to this, but thanks to everyone who helped me with this :)Hennie
If you do find a working solution, a self-answer with your results to confirm would help. Hope that works out!Padilla
H
5

There are multiple ways to solve this problem but this one fits me the best.

I created a new function called serialize_foo_option that looks like the following:

pub fn serialize_foo_option<S>(
    maybe_foo: &Option<Foo>,
    serializer: S,
) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    let mut map = serializer.serialize_map(some_len)?;
    if let Some(foo) = maybe_foo {
        map.serialize_entry("key", &foo)?;
    }
    map.end()
}

This way if None is provided nothing is serialized.

Hennie answered 3/10, 2022 at 6:31 Comment(1)
github.com/serde-rs/serde/issues/1301 this issue has an alternative solution which works quite well, although it also requires a custom functionIssus
D
1

This worked for me:

use serde::{Deserialize, Deserializer, Serialize, Serializer};

#[derive(Deserialize, Serialize)]
struct Golden {
    #[serde(serialize_with = "from_evil", deserialize_with = "to_evil")]
    one: Option<Evil>,
}

struct Evil {
    two: String,
}

fn from_evil<S: Serializer>(v: &Option<Evil>, serializer: S) -> Result<S::Ok, S::Error> {
    let v = v.as_ref().and_then(|v| Some(v.two.clone()));
    Option::<String>::serialize(&v, serializer)
}

fn to_evil<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Option<Evil>, D::Error> {
    Option::<String>::deserialize(deserializer)
        .and_then(|str| Ok(str.and_then(|str| Some(Evil { two: str }))))
}

Also make sure to include required serde features

[dependencies]
serde = {version = "1.0.196", features = ["serde_derive"] }
Dimond answered 5/2 at 12:29 Comment(0)
B
0

If you control the serializer function, you can avoid having to write an addition function for Option. This is especially important if you have many wrappers, e.g. Vec, Option, etc.. You can do that with serde_with, which has a more composable system of [de]serialization:

use serde::{Serialize, Serializer};

struct SerializeFoo;
impl serde_with::SerializeAs<Foo> for SerializeFoo {
    fn serialize_as<S: Serializer>(source: &Foo, serializer: S) -> Result<S::Ok, S::Error> {
        // Serialize here...
    }
}

#[serde_with::serde_as]
#[derive(Serialize)]
struct Bar {
    #[serde_as(as = "SerializeFoo")]
    pub x: Foo,
    #[serde_as(as = "Option<SerializeFoo>")]
    // This works as normal:
    // #[serde(skip_serializing_if = "Option::is_none")]
    pub y: Option<Foo>,
}
Basie answered 8/2 at 18:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.