Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to serialize Option<Vec<DateTime>> field to bson?

Tags:

rust

serde

I have a struct defined like the following (other fields removed for brevity):

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

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Test {
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(with = "bson::serde_helpers::chrono_datetime_as_bson_datetime")]
    pub time_period: Option<Vec<DateTime<Utc>>>
}

And I'm using the following in my Cargo.toml:

[dependencies]
bson = { version = "^2.4", default-features = false, features = [ "chrono-0_4" ] }
chrono = "^0.4"
serde = { version = "^1.0", default-features = false, features = [ "derive" ] }

But the serde derivations throw error because it expects a DateTime object:

error[E0308]: mismatched types
   --> src/main.rs:4:39
    |
4   | #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
    |                                       ^^^^^^^^^
    |                                       |
    |                                       expected struct `chrono::DateTime`, found enum `std::option::Option`
    |                                       arguments to this function are incorrect
    |
    = note: expected reference `&chrono::DateTime<Utc>`
               found reference `&'__a std::option::Option<Vec<chrono::DateTime<Utc>>>`
note: function defined here
   --> /home/kmdreko/.cargo/registry/src/github.com-1ecc6299db9ec823/bson-2.4.0/src/serde_helpers.rs:296:12
    |
296 |     pub fn serialize<S: Serializer>(
    |            ^^^^^^^^^
    = note: this error originates in the derive macro `Serialize` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0308]: mismatched types
 --> src/main.rs:4:50
  |
4 | #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
  |                                                  ^^^^^^^^^^^ expected enum `std::option::Option`, found struct `chrono::DateTime`
  |
  = note: expected enum `std::option::Option<Vec<chrono::DateTime<Utc>>>`
           found struct `chrono::DateTime<Utc>`
  = note: this error originates in the macro `try` (in Nightly builds, run with -Z macro-backtrace for more info)

Any ideas how to serialize optional vector of DateTime objects?

like image 792
terett Avatar asked Sep 20 '25 12:09

terett


2 Answers

Serde's with attribute can only be used if your function/module expects the exact type of the field. That is not possible to change and sometimes you see *_opt functions/modules to provide support for Options, but never for arbitrary nesting.

The bson crate has a feature to use serde_with to work around that. You need to enable the serde_with feature of bson and import serde_with v1 into your code. Note the extra default attribute to serde. That is unnecessary for v2 of serde_with, but bson is still on v1.

#[serde_with::serde_as]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Test {
    #[serde(skip_serializing_if = "Option::is_none", default)]
    #[serde_as(as = "Option<Vec<bson::DateTime>>")]
    pub time_period: Option<Vec<DateTime<Utc>>>
}
[dependencies]
bson = { version = "^2.4", default-features = false, features = [ "chrono-0_4", "serde_with" ] }
chrono = "^0.4"
serde = { version = "^1.0", default-features = false, features = [ "derive" ] }
serde_with = "1"

Run on Rustexplorer

like image 70
jonasbb Avatar answered Sep 23 '25 12:09

jonasbb


Why doesn't the OP's code work

The bson::serde_helpers::chrono_datetime_as_bson_datetime module contains helper functions that (de)serializes a single chrono::DateTime object, so it doesn't work with the field of type Option<Vec<DateTime<Utc>>>.

Solution using nested struct

To (de)serialize such data, you can define a struct TestInner that contains the inner DateTime<Utc> object, along with the #[serde(transparent)] container attribute so that it's (de)serialized in the same way as a single DateTime<Utc> object. For example, the struct Test in the question can be changed into something like this.

#[derive(Debug, Serialize, Deserialize)]
pub struct Test {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub time_period: Option<Vec<TestInner>>,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(transparent)]
pub struct TestInner {
    #[serde(with = "bson::serde_helpers::chrono_datetime_as_bson_datetime")]
    datetime: DateTime<Utc>,
}
like image 42
kotatsuyaki Avatar answered Sep 23 '25 12:09

kotatsuyaki