[serde] How to deserialize string value (not json)? Is it possible?

Hi Community

The snippet below implements the ser / deserialization for protobuf Timestamp Package google.protobuf  |  Protocol Buffers  |  Google Developers

I wonder if I can deserialize the TImestamp from just timestamp string value, not JSON-like string.
My test is failed.

Thank you in advance!

use chrono::prelude::*;
use serde::de::{self, Deserialize, Deserializer, Visitor};
use serde::ser::{Serialize, Serializer};
use serde_json;


#[derive(Debug, PartialEq)]
struct Timestamp {
    seconds: i64,
    nanos: i32,
}

impl Serialize for Timestamp {
    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
    where
        S: Serializer,
    {
        DateTime::<Utc>::from_utc(
            NaiveDateTime::from_timestamp(self.seconds, self.nanos as u32),
            Utc,
        )
        .to_rfc3339()
        .serialize(serializer)
    }
}

impl<'de> Deserialize<'de> for Timestamp {
    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
    where
        D: Deserializer<'de>,
    {
        struct TimestampVisitor;

        impl<'de> Visitor<'de> for TimestampVisitor {
            type Value = Timestamp;

            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                formatter.write_str("Timestamp in RFC3339 format")
            }

            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
            where
                E: de::Error,
            {
                chrono::DateTime::parse_from_rfc3339(value)
                    .map(|dt| Timestamp{ seconds: dt.timestamp(), nanos: dt.timestamp_subsec_nanos() as i32})
                    .map_err(E::custom)
            }
        }
        deserializer.deserialize_str(TimestampVisitor)
    }
}

fn main() {
    let ts = Timestamp {
        seconds: 123456,
        nanos: 123
    };
    println!(
        "{:?} is {:?}",
        ts,
        serde_json::to_string(&ts)
    );

    let ts_string = "2021-12-27T07:00:00.000Z";
    println!(
        "{:?} is {:?}",
        ts_string,
        serde_json::from_str::<Timestamp>(ts_string).unwrap()
    );
}

(Playground)

Output:

Timestamp { seconds: 123456, nanos: 123 } is Ok("\"1970-01-02T10:17:36.000000123+00:00\"")

Errors:

   Compiling playground v0.0.1 (/playground)
    Finished dev [unoptimized + debuginfo] target(s) in 3.40s
     Running `target/debug/playground`
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error("invalid type: integer `2021`, expected Timestamp in RFC3339 format", line: 1, column: 4)', src/main.rs:69:54
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Your ts_string variable does not contain valid JSON. You need another set of quotes to create a JSON string literal. With that change the code on the playground works for me.

let ts_string = "\"2021-12-27T07:00:00.000Z\"";
1 Like

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.