Serde deserialize_with in generic struct field

I have a generic struct:

#[macro_rules_attribute::macro_rules_derive(crate::common::macros::schemars_rename_generics)]
#[derive_args(<FloatPayloadType> => "Range", <DateTimePayloadType> => "DatetimeRange")]
#[derive(Debug, Deserialize, Serialize, Default, Clone, PartialEq)]
#[serde(rename_all = "snake_case")]
pub struct Range<T> {
    /// point.key < range.lt
    pub lt: Option<T>,
    /// point.key > range.gt
    pub gt: Option<T>,
    /// point.key >= range.gte
    pub gte: Option<T>,
    /// point.key <= range.lte
    pub lte: Option<T>,
}

how to use deserialize_with on these fields?

How do you expect it to deserialize? As in, what kind of input strings deserialize to which types?

In any case, using a combination of this URLO post and this SO post, I think I have your answer; you probably want to combine #[serde(deserialize_with)] with #[serde(default)]:

Rust Playground

#[derive(Debug, Deserialize, Serialize, Default, Clone, PartialEq)]
struct A<T: DeserializeOwned> {
    #[serde(default)]
    #[serde(deserialize_with = "callback_opt")]
    lt: Option<T>
}

pub fn callback_opt<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
where
    D: Deserializer<'de>,
    T: Deserialize<'de>
{
    Option::<T>::deserialize(deserializer)
}

Almost certainly you'll have to have a trait bound on T, I would think; otherwise you wouldn't be able to assure that you can deserialize T, and thus not be certain to deserialize the struct, unless you write your own custom impl Deserialize.

Thank u for ur reply XD.
Actually there are two types, float and chrono::Datetime<chrono::Utc>. For the formmer, I want to use the default deserializer, while for the latter, I want to do it more flexibly, I want to deserialize it from String by myself. BTW, i don't want to use a wrapper of Datetime, since it will affect many of my codes.

Hrmm...quickly exceeding the limits of my experience, but...

My first idea would be to write explcit impl Deserialize implementations for each type of T, i.e. impl Deserialize for Range<f64> and for Datetime<Utc>. Then in the former you can just call the existing deserializers, and for the latter you can implement the custom logic.

Another option might be to have a wrapped enum around f64 and DateTime, and implement TryInto<Range<f64>> for Range<WrappedEnum>, so that you don't have to change the rest of your code. That's just a brainstorm, though, I don't know how much easier/better that would be.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.