How to use chrono::DateTime<Utc> with schemars::JsonSchema? - rust

I'm trying to implement JsonSchema for a struct like this:
use chrono::{DateTime, Utc};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Default, Clone, JsonSchema)]
pub struct ArticleResponse {
pub pub_time: Option<DateTime<Utc>>,
}
But this outputs an error when compiling:
error[E0277]: the trait bound `DateTime<Utc>: JsonSchema` is not satisfied
--> src/main.rs:4:50
|
4 | #[derive(Serialize, Deserialize, Default, Clone, JsonSchema)]
| ^^^^^^^^^^ the trait `JsonSchema` is not implemented for `DateTime<Utc>`
What should I do to implement JsonSchema for ArticleResponse?

To quote the docs:
For example, to implement JsonSchema on types from chrono, enable it as a feature in the schemars dependency in your Cargo.toml like so:
[dependencies]
schemars = { version = "0.8", features = ["chrono"] }

Related

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

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

How to serialize and deserialize chrono::Duration?

In my current project I'm trying to store a chrono::Duration in a configuration struct, which will be serialized and deserialized occasionally using serde_json.
Unfortunately, it appears that Serialize and Deserialize aren't implemented for chrono::Duration. That said, chrono says it has support for serde via one of its optional features. I tried using this method, but now the compiler is complaining about return methods:
error[E0308]: mismatched types
--> src/config.rs:6:10
|
6 | #[derive(Serialize, Deserialize, Debug, Clone)]
| ^^^^^^^^^ expected struct `DateTime`, found struct `chrono::Duration`
|
= note: expected reference `&DateTime<Utc>`
found reference `&'__a chrono::Duration`
= 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/config.rs:6:21
|
6 | #[derive(Serialize, Deserialize, Debug, Clone)]
| ^^^^^^^^^^^ expected struct `chrono::Duration`, found struct `DateTime`
|
= note: expected struct `chrono::Duration`
found struct `DateTime<Utc>`
= note: this error originates in the macro `try` (in Nightly builds, run with -Z macro-backtrace for more info)
Why is this happening? What can I do to fix it?
Here's the code in question:
use serde::{Serialize, Deserialize};
use chrono::{DateTime, Duration, Utc};
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Config {
pub dest_ip: String,
#[serde(borrow)]
pub include_paths: Vec<&'static std::path::Path>,
pub exclude_paths: Vec<&'static std::path::Path>,
#[serde(with = "chrono::serde::ts_seconds")]
pub time_between_checks: Duration,
}
Also, here's the relevant bits of Cargo.toml:
serde_json = "1.0.72"
serde = { version = "1.0.130", features = ["derive"] }
chrono = { version = "0.4.19", features = ["serde"]}
You can use serde_with::DurationSeconds for serialization. It works identical to ts_seconds but supports more types and serialization forms.
#[serde_with::serde_as]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Config {
// ...
#[serde_as(as = "serde_with::DurationSeconds<i64>")]
pub time_between_checks: Duration,
}

Serde's Serialize implementation not found for Rocket's UUID

I'm trying to create a custom struct using the UUID struct from Rocket as a field type. I want it to be serialized using Serde in order to convert it to JSON easily.
When trying to do this, I get an error:
error[E0277]: the trait bound `rocket_contrib::UUID:
model::event::_IMPL_DESERIALIZE_FOR_Event::_serde::Serialize` is not
satisfied
--> src/service/document.rs:4:10
|
4 | #[derive(Serialize, Deserialize)]
| ^^^^^^^^^ the trait
`model::event::_IMPL_DESERIALIZE_FOR_Event::_serde::Serialize` is not
implemented for `rocket_contrib::UUID`
|
= note: required by `model::event::_IMPL_DESERIALIZE_FOR_Event::_serde::ser::SerializeStruct::serialize_field`
error[E0277]: the trait bound `rocket_contrib::UUID:
model::event::_IMPL_DESERIALIZE_FOR_Event::_serde::Deserialize<'_>` is not satisfied
--> src/service/document.rs:4:21
|
4 | #[derive(Serialize, Deserialize)]
| ^^^^^^^^^^^ the trait
`model::event::_IMPL_DESERIALIZE_FOR_Event::_serde::Deserialize<'_>` is not implemented for `rocket_contrib::UUID`
|
= note: required by `model::event::_IMPL_DESERIALIZE_FOR_Event::_serde::de::SeqAccess::next_element`
error[E0277]: the trait bound `rocket_contrib::UUID: model::event::_IMPL_DESERIALIZE_FOR_Event::_serde::Deserialize<'_>` is not satisfied
--> src/service/document.rs:4:21
|
4 | #[derive(Serialize, Deserialize)]
| ^^^^^^^^^^^ the trait `model::event::_IMPL_DESERIALIZE_FOR_Event::_serde::Deserialize<'_>` is not implemented for `rocket_contrib::UUID`
|
= note: required by `model::event::_IMPL_DESERIALIZE_FOR_Event::_serde::de::MapAccess::next_value`
My struct:
#[derive(Serialize, Deserialize)]
pub struct Document {
id: UUID,
user_id: UUID,
created: i64,
updated: i64,
text: String
}
My imports:
[dependencies]
rocket = "0.3.17"
rocket_codegen = "0.3.17"
serde_derive = "1.0.80"
serde = "1.0.80"
chrono = "0.4"
[dependencies.rocket_contrib]
version = "0.3.17"
default-features = false
features = ["json", "uuid", "serde"]
Endpoint where I use the struct:
#[get("/document/<id>")]
pub fn get_document(id: UUID) -> status::Accepted<Json<Document>> {
status::Accepted(Some(Json(document::get_document(id))))
}
I've checked all dependencies, and I have the serde feature enabled in rocket_contrib. I've run out of ideas what to check next.
rocket_contrib::UUID does not implement Serialize:
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct UUID(uuid_ext::Uuid);
If the type doesn't implement Serialize, you can't make it.
As mcarton points out:
you still can implement Serialize for your type, you simply can't derive it and will have to implement it by hand.
That could look something like:
#[derive(Serialize, Deserialize)]
pub struct Document {
#[serde(with = "my_uuid")]
id: UUID,
#[serde(with = "my_uuid")]
user_id: UUID,
created: i64,
updated: i64,
text: String,
}
mod my_uuid {
use rocket_contrib::UUID;
use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
use std::str::FromStr;
pub fn serialize<S>(val: &UUID, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
val.to_string().serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<UUID, D::Error>
where
D: Deserializer<'de>,
{
let val: &str = Deserialize::deserialize(deserializer)?;
UUID::from_str(val).map_err(D::Error::custom)
}
}
See also:
How to transform fields during serialization using Serde?
How to transform fields during deserialization using Serde?
Is there a way for me to use #[derive] on a struct or enum from a library without editing the actual library's source code?
How do I implement a trait I don't own for a type I don't own?
Add Serialize attribute to type from third-party lib

How to make the #![no_main] attribute conditional? [duplicate]

I have added a feature in my crate which adds serde support. However, I don't quite understand how to use it properly:
// #[derive(Debug, Serialize, Deserialize, Clone)] // goes to:
#[derive(Debug, Clone)]
#[cfg(feature = "serde_support")]
#[derive(Serialize, Deserialize)]
pub struct MyStruct;
This code treats everything below cfg(feature) as conditionally compiled, so without my serde_support feature my crate does not have MyStruct also.
I have tried to wrap it with braces but it gives another error:
Code:
#[derive(Debug, Clone)]
#[cfg(feature = "serde_support")] {
#[derive(Serialize, Deserialize)]
}
pub struct MyStruct;
Error:
error: expected item after attributes
--> mycrate/src/lib.rs:65:33
|
65 | #[cfg(feature = "serde_support")] {
| ^
So how to do this?
You can use the cfg_attr(a, b) attribute:
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
pub struct MyStruct;
It's described in the Rust reference about "conditional compilation":
#[cfg_attr(a, b)]
item
Will be the same as #[b] item if a is set by cfg, and item otherwise.

"cannot find value __collect" when adding flatten to an enum struct variant

I have the following code:
extern crate serde;
#[macro_use]
extern crate serde_derive;
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct Base {
bold: Option<bool>,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Component {
String {
#[serde(flatten)] base: Base,
text: String,
},
}
This gives me a compiler error:
error[E0425]: cannot find value `__collect` in this scope
--> src/main.rs:10:28
|
10 | #[derive(Debug, Serialize, Deserialize)]
| ^^^^^^^^^^^ did you mean `__content`?
error[E0277]: the trait bound `<__S as serde::Serializer>::SerializeStruct: serde::ser::SerializeMap` is not satisfied
--> src/main.rs:10:17
|
10 | #[derive(Debug, Serialize, Deserialize)]
| ^^^^^^^^^ the trait `serde::ser::SerializeMap` is not implemented for `<__S as serde::Serializer>::SerializeStruct`
|
= help: consider adding a `where <__S as serde::Serializer>::SerializeStruct: serde::ser::SerializeMap` bound
= note: required because of the requirements on the impl of `serde::Serializer` for `serde::private::ser::FlatMapSerializer<'_, <__S as serde::Serializer>::SerializeStruct>`
= note: required by `serde::Serialize::serialize`
If I change my code to this, it compiles fine:
extern crate serde;
#[macro_use]
extern crate serde_derive;
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct Base {
bold: Option<bool>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Striii {
#[serde(flatten)]
base: Base,
text: String,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Component {
String(Striii),
}
I don't understand the compiler error or why it shouldn't work. Does Serde have no support for field attributes inside enum struct variants?
This is a known issue with Serde for both serialization and deserialization. There are no listed workarounds.

Resources