Szegoo
Szegoo

Reputation: 297

How to serialize an option with serde?

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.

Upvotes: 6

Views: 4755

Answers (3)

Chayim Friedman
Chayim Friedman

Reputation: 71005

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>,
}

Upvotes: 0

IntegratedHen
IntegratedHen

Reputation: 473

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"] }

Upvotes: 1

Szegoo
Szegoo

Reputation: 297

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.

Upvotes: 5

Related Questions