How to deserialize a custom struct correctly with Serde?

I am trying to deserialize a struct of external crate with #[serde(with = "")]

use chrono::{DateTime, NaiveDateTime, Utc};
use ormx::Table;
use serde::ser::{SerializeStruct, Serializer};
use serde::{Deserialize, Serialize};
use sqlx::postgres::types::PgRange;
use sqlx::{Pool, Postgres};

#[derive(Debug, Clone, Serialize, Table)]
#[ormx(table = "sessions", id = id, insertable, deletable)]
pub struct Session {
    #[ormx(default)]
    pub id: i32,
    pub name: String,
    #[serde(with = "crate::utils::pg_range")]
    pub duration: PgRange<NaiveDateTime>,
}

And this is my serializer module:

use chrono::{NaiveDateTime};
use serde::ser::{SerializeStruct, Serializer};
use serde::{Deserialize, Serialize};
use sqlx::postgres::types::PgRange;
use serde::de::{Deserialize, Deserializer, Visitor, SeqAccess, MapAccess};

pub fn serialize<S>(range: &PgRange<NaiveDateTime>, s: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    let mut state = s.serialize_struct("PgRange", 2)?;
    state.serialize_field("start", &range.start)?;
    state.serialize_field("end", &range.end)?;
    state.end()
}

pub fn deserialize<'de, D>(d: D) -> Result<PgRange<NaiveDateTime>, D::Error> where D: Deserializer<'de> {
    Deserialize::deserialize(d)?
}

I am getting an error like this now, which I don't understand:

error[E0277]: the trait bound `<D as Deserializer<'de>>::Error: employees::_::_serde::Deserialize<'_>` is not satisfied
   --> src/utils/pg_range.rs:18:5
    |
18  |     Deserialize::deserialize(d)?
    |     ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `employees::_::_serde::Deserialize<'_>` is not implemented for `<D as Deserializer<'de>>::Error`
    |
    = note: required because of the requirements on the impl of `employees::_::_serde::Deserialize<'_>` for `Result<PgRange<NaiveDateTime>, <D as Deserializer<'de>>::Error>`
note: required by a bound in `employees::_::_serde::Deserialize::deserialize`
   --> /home/hugosum/.cache/cargo/registry/src/github.com-1ecc6299db9ec823/serde-1.0.130/src/de/mod.rs:539:12
    |
539 |         D: Deserializer<'de>;
    |            ^^^^^^^^^^^^^^^^^ required by this bound in `employees::_::_serde::Deserialize::deserialize`
help: consider further restricting the associated type
    |
17  | pub fn deserialize<'de, D>(d: D) -> Result<PgRange<NaiveDateTime>, D::Error> where D: Deserializer<'de>, <D as Deserializer<'de>>::Error: employees::_::_serde::Deserialize<'_> {
    |                                                                                                        ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

How should I fix this and deserialize correctly?

Your deserialize function already returns a Result and so does Deserialize::deserialize(). There is no need for a question mark operator, you should just return the result of the call directly.

Ah right. I have removed the question mark and it should return Result for me, but I am getting this error:

error[E0277]: the trait bound `PgRange<NaiveDateTime>: repository::employees::_::_serde::Deserialize<'_>` is not satisfied
   --> src/utils/pg_range.rs:20:5
    |
20  |     Deserialize::deserialize(d)
    |     ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `repository::employees::_::_serde::Deserialize<'_>` is not implemented for `PgRange<NaiveDateTime>`

But I am implementing the deserialize function here? Did I missed anything?

You are not implementing the deserialize trait. serde(with) is not magic: it just makes the generated impl use the provided function instead of the Deserialize trait of the field type. It doesn't make the field type implement Deserialize itself; its very point is exactly to handle fields which are not Deserialize.

It looks like you are trying to implement the deserialization in terms of itself - that doesn't make sense and it's not going to work. You will have to break down the type into parts (fields) that themselves implement Deserialize and perform the deserialization using those parts.

2 Likes

Thanks for your comment! It points me to the right direction and I am able to deserialize it like this now:

use chrono::NaiveDateTime;
use serde::de::{Deserializer, Error, MapAccess, SeqAccess, Visitor};
use serde::ser::{SerializeStruct, Serializer};
use sqlx::postgres::types::PgRange;
use std::fmt;
use std::ops::Range;

// Leaving out the serialize function

pub fn deserialize<'de, D>(d: D) -> Result<PgRange<NaiveDateTime>, D::Error>
where
    D: Deserializer<'de>,
{
    #[derive(serde::Deserialize)]
    #[serde(field_identifier, rename_all = "lowercase")]
    enum Field {
        Start,
        End,
    }

    struct DurationVisitor;

    impl<'de> Visitor<'de> for DurationVisitor {
        type Value = PgRange<NaiveDateTime>;

        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            formatter.write_str("struct PgRange")
        }

        fn visit_seq<V>(self, mut seq: V) -> Result<PgRange<NaiveDateTime>, V::Error>
        where
            V: SeqAccess<'de>,
        {
            let start = seq
                .next_element()?
                .ok_or_else(|| Error::invalid_length(0, &self))?;
            let end = seq
                .next_element()?
                .ok_or_else(|| Error::invalid_length(1, &self))?;
            Ok(PgRange::from(Range { start, end }))
        }

        fn visit_map<V>(self, mut map: V) -> Result<PgRange<NaiveDateTime>, V::Error>
        where
            V: MapAccess<'de>,
        {
            let mut start = None;
            let mut end = None;
            while let Some(key) = map.next_key()? {
                match key {
                    Field::Start => {
                        if start.is_some() {
                            return Err(Error::duplicate_field("start"));
                        }
                        start = Some(map.next_value()?);
                    }
                    Field::End => {
                        if end.is_some() {
                            return Err(Error::duplicate_field("end"));
                        }
                        end = Some(map.next_value()?);
                    }
                }
            }
            let start = start.ok_or_else(|| Error::missing_field("start"))?;
            let end = end.ok_or_else(|| Error::missing_field("end"))?;
            Ok(PgRange::from(Range { start, end }))
        }
    }

    const FIELDS: &'static [&'static str] = &["start", "end"];
    d.deserialize_struct("PgRange", FIELDS, DurationVisitor)
}

And I have this as output:

[
  {
    "id": 1,
    "name": "Session A",
    "duration": {
      "start": {
        "Included": "2021-10-19T09:00:00"
      },
      "end": {
        "Included": "2021-10-19T10:00:00"
      }
    }
  }
]

So how I have Included there, and I don't understand why it is introduced by my code? Where should I look for this issue?

Well, if you are serializing the fields of PgRange, then it must come from whatever the type of those fields is. I never used the Postgres crate, but since we are talking about a Range, I'm guessing it's an enum RangeBounds<T> { Included(T), Excluded<T> }, which can generically represent ranges that are closed on either, neither, or both sides.

Right that points me to the right direction! I think I have to use #serde[untagged]

https://serde.rs/enum-representations.html#untagged

But since that enum is from std::ops, how can I use this attribute macro on it?

You can't. (Also, it would be wrong. Since all variants are of the same type, it would always be the first variant that would be deserialized, potentially — and silently! — changing the kind of the interval.)

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.