Implementing a deserialize function in serde with `deserialize_with`

I'm writing a fairly simple application that I would like to grant better type safety and functionality to.

I have an XML file which looks somewhat like this:

<device timestamp="1688712542">
</device>

When I attempt to deserialize using serde-xml-rs, I can do so with the following derive definition:

use serde::{Serialize, Serializer, Deserialize, Deserializer};

#[derive(Debug, Serialize, Deseriailze)]
pub struct Device {
    pub timestamp: i64
}

I'd like to use chrono and specifically chrono::NaiveDateTime to parse the Unix epoch seconds into a NaiveDateTime and to serialize a NaiveDateTime into an i64.

To this end, I have followed the docs on serde derive's field attributes: Field attributes ยท Serde

use chrono::NaiveDateTime;
use serde::{Serialize, Serializer, Deserialize, Deserializer};

#[derive(Debug, Serialize, Deserialize)]
pub struct Device {
    #[serde(serialize_with = "serialize_ndt", deserialize_with = "deserialize_ndt")]
    pub timestamp: NaiveDateTime,
}

/// Serialize a `chrono::NaiveDateTime` into a string representation of the seconds since the Unix
/// epoch.
pub fn serialize_ndt<S>(
    value: &NaiveDateTime,
    s: S,
) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    s.serialize_str(value.timestamp().to_string().as_str())
}

/// Deserialize a Unix epoch timestamp into a `chrono::NaiveDateTime`.
pub fn deserialize_naive_date_timestamp<'de, D>(d: D) -> Result<NaiveDateTime, D::Error>
where
    D: Deserializer<'de>,
{
    // FIXME it is not possible to call a Deserializer function without a serde::de::Visitor
    let seconds: i64 = d.deserialize_str(ERROR)?;

    NaiveDateTime::from_timestamp_opt(seconds, 0).ok_or_else(|| {
        Err(D::Error::custom(
            "unable to construct NaiveDateTime from i64",
        ))
    })
}

Serialization compiles fine, I have not tested it yet, but I can't compile the deserializer function, due to each of serde::Deserializer's methods requiring a serde::de::Visitor.

I have followed the function definitions from serde's documentation, which are:

  • for serialization: fn<S>(&T, S) -> Result<S::Ok, S::Error> where S: Serializer
  • for deserialization: fn<'de, D>(D) -> Result<T, D::Error> where D: Deserializer<'de>

Given a Deserializer<'de> and using the function method as specified in the documentation, how can I deserialize data? Literally every function on serde::Deserializer require a Visitor.

Just use i64::deserialize(d). Playground.

1 Like

Thank you, I always forget to check for trait implementations on items not within a crate such as std.

Well, this is a common pattern โ€“ if you essentially want to wrap a type in the newtype pattern, then you just forward to it.

If you think about it, primitive types and std's types in general must implement Deserialize (of course the impls are defined by Serde, not by std), because otherwise they would be impossible to use in derived Deserialize impls.

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.