Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't serialize/deserialize struct which contains Option<chrono::DateTime<Utc>> field. #303

Closed
WindSoilder opened this issue Sep 11, 2021 · 6 comments
Assignees
Labels
tracked-in-jira Ticket filed in Mongo's Jira system

Comments

@WindSoilder
Copy link

Take the following struct as example:

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;

#[derive(Debug, Serialize, Deserialize)]
pub struct Authorization {
    #[serde(with = "bson::serde_helpers::uuid_as_binary")]
    client_id: Uuid,
    dbs: Vec<String>,
    colls: Option<Vec<String>>,
    #[serde(with = "bson::serde_helpers::chrono_datetime_as_bson_datetime")]
    start_date: Option<DateTime<Utc>>,
    #[serde(with = "bson::serde_helpers::chrono_datetime_as_bson_datetime")]
    end_date: Option<DateTime<Utc>>,
    name: String,
}

When I try to complile, I get the following error message:

mismatched types
= note: expected reference `&chrono::DateTime<Utc>`
              found reference `&'__a std::option::Option<chrono::DateTime<Utc>>`

I think this should work for start_date and end_date field just like colls field in the given Authorization struct, sorry for that I go through documentation and can't find out an answer, am I miss something?

@patrickfreed
Copy link
Contributor

patrickfreed commented Sep 16, 2021

Unfortunately, using serde(with = "...") to compose with Option is not currently supported by serde (see serde-rs/serde#723). As a workaround, you can manually implement this via a custom (de)serialize module:

#[derive(Deserialize, Serialize, Debug)]
struct Dd {
    #[serde(with = "opt_chrono_datetime_as_bson_datetime")]
    #[serde(default)]
    date: Option<chrono::DateTime<Utc>>,
}

mod opt_chrono_datetime_as_bson_datetime {
    use chrono::Utc;
    use serde::{Deserialize, Deserializer, Serialize, Serializer};
    use mongodb::bson;

    #[derive(Serialize, Deserialize)]
    struct Helper(
        #[serde(with = "bson::serde_helpers::chrono_datetime_as_bson_datetime")]
        chrono::DateTime<Utc>,
    );

    pub fn serialize<S>(
        value: &Option<chrono::DateTime<Utc>>,
        serializer: S,
    ) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        value.map(Helper).serialize(serializer)
    }

    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<chrono::DateTime<Utc>>, D::Error>
    where
        D: Deserializer<'de>,
    {
        let helper: Option<Helper> = Option::deserialize(deserializer)?;
        Ok(helper.map(|Helper(external)| external))
    }
}

Now there does exist the serde_with crate which would make this much more ergonomic, but we currently don't have support for it. I filed RUST-1024 to track the work for introducing that integration. Once that ticket is done, the following should be possible:

#[serde_as]
#[derive(Debug, Serialize, Deserialize)]
struct MyData {
    #[serde_as(as = "Option<bson::DateTime>")]
    date: Option<chrono::DateTime<Utc>>
}

@patrickfreed patrickfreed added the tracked-in-jira Ticket filed in Mongo's Jira system label Sep 16, 2021
@xxated
Copy link

xxated commented Nov 27, 2021

What if you try to import DateTime from MongoDB instead of chrono?
use mongodb::{
bson::{DateTime},
};

@patrickfreed
Copy link
Contributor

Yep, using a bson::DateTime works just fine for this use case as well. The only consideration with that is you'd have to convert it to a chrono::DateTime any time you needed to use the chrono functionality.

BTW, the serde_with integration was contained in the 2.1.0-beta release, so the second example in my previous response should work now. Note that you'll need to enable the serde_with feature flag to use it. Going to close this issue out now that a workaround exists.

@ericpko
Copy link

ericpko commented Jul 28, 2022

@patrickfreed
I'm actually still getting an error:
error[E0277]: the trait bound bson::DateTime: SerializeAs<_> is not satisfied
error[E0277]: the trait bound bson::DateTime: DeserializeAs<'_, _> is not satisfied

even though I've tried your second option:

#[serde_as]
#[derive(Debug, Serialize, Deserialize)]
struct MyData {
    #[serde_as(as = "Option<bson::DateTime>")]
    date: Option<chrono::DateTime<Utc>>
}

serde_with

bson_DateTime_error

serde_with_cargo_toml

Maybe I'm doing something obviously wrong that I'm not seeing?

@jonasbb
Copy link
Contributor

jonasbb commented Jul 28, 2022

@ericpko You need to use the same major version of serde_with as bson, currently this is v1. Be aware that serde_as got smarter in v2 regarding Option types. You will need to apply #[serde(default)] on these Option fields.

@ericpko
Copy link

ericpko commented Jul 28, 2022

@jonasbb This worked! Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
tracked-in-jira Ticket filed in Mongo's Jira system
Projects
None yet
Development

No branches or pull requests

5 participants