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>,
}
Related
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"] }
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,
}
I'm rather new to Rust, but was looking to use it for some numerics work and started exploring the ndarray crate a bit. Doing so, however, I got a bit stumped trying to derive serde::Serialize and serde::Deserialize for structs containing arrays.
In particular, I tried to compile the following snippet but got an error in doing so:
extern crate serde;
use ndarray::{ Array1 };
use serde::{ Serialize, Deserialize };
#[derive(Serialize, Deserialize)]
pub struct Canary {
pub xs: Array1<f64>
}
error[E0277]: the trait bound `ndarray::ArrayBase<ndarray::OwnedRepr<f64>, ndarray::Dim<[usize; 1]>>: serde::Serialize` is not satisfied
--> src/lib.rs:40:5
|
40 | pub xs: Array1<f64>
| ^^^ the trait `serde::Serialize` is not implemented for `ndarray::ArrayBase<ndarray::OwnedRepr<f64>, ndarray::Dim<[usize; 1]>>`
|
= note: required by `serde::ser::SerializeStruct::serialize_field`
# Cargo.toml
[dependencies]
ndarray = { version = "0.12.1", features = ["serde"] }
serde = { version = "1.0", features = ["derive"] }
Looking at the bounds on impl<A, D, S> Serialize for ArrayBase<S, D> where A: Serialize, D: Dimension + Serialize, S: Data<Elem = A>, I'm a bit confused as to what the error is, since A = f64 implements Serialize, and since D = Dim<[usize; 1]> implements both Dimension and Serialize. Is there something I'm missing in order to derive serialization for structs containing arrays? Thanks!
The latest version of ndarray is 0.13.1.
Updating the version in your Cargo.toml should fix the issue:
[dependencies]
ndarray = { version = "0.13.1", features = ["serde"] }
This answer was suggested by #Locke in a comment. I just created a small answer for future visitors.
I am trying to declare a struct that contains another struct of any given type that can be Deserialize and Serialize.
#[derive(Debug, Serialize, Deserialize)]
pub struct Foo<T: Deserialize + Serialize> {
pub data: T,
}
Rust playground.
For that, I have tried to use trait bounds, using traits such as DeserializeOwned or Deserialize. Both have failed at compilation time with the following errors:
error[E0283]: type annotations required: cannot resolve `T: serde::Deserialize<'de>`
--> src/main.rs:9:28
|
9 | #[derive(Debug, Serialize, Deserialize)]
| ^^^^^^^^^^^
|
= note: required by `serde::Deserialize`
error[E0637]: `&` without an explicit lifetime name cannot be used here
--> src/main.rs:10:19
|
10 | pub struct Foo<T: Deserialize + Serialize> {
| ^^^^^^^^^^^ explicit lifetime name needed here
I faced errors trying to add a lifetime since I am not using storing a reference but a value.
What is the most idiomatic way of declaring this type of struct?
Just don't place the bounds on the type:
use serde::{Deserialize, Serialize}; // 1.0.91
#[derive(Debug, Serialize, Deserialize)]
pub struct Foo<T> {
pub data: T,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Boo {
pub a: i64,
}
fn main() {
let data = Boo { a: 1 };
let wrap = Foo { data };
println!("{:?}", wrap);
}
Then, place the bounds on methods where you need that behavior:
fn use_it<T>(foo: Foo<T>)
where
Foo<T>: Serialize,
{
// ...
}
I found a solution thanks to a member of the Rust Discord who referred me to the following Github issue. The trick is not to use trait bounds but attribute bounds.
#[derive(Debug, Serialize, Deserialize)]
pub struct Foo<T> {
#[serde(bound(
serialize = "T: Serialize",
deserialize = "T: Deserialize<'de>",
))]
pub data: T,
}
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