Time & serde_json


#1

Hello everyone ! o/
I’ve been playing with Rust on a couple projects … and in a recent one I couldn’t manage to get serde_json and the time crate to play nice with each other.

I use a struct which has a time::Timespec inside, which serde doesn’t like (because it doesn’t have the Serialize/Deserialize implemented). I tried implementing it (by making use of strftime and strptime), but I’ve learned that I can’t implement traits on types that are off-crate.

I’m kinda running out of ideas on what to do without making a wrapper on top of Timespec (thing which I really, really want to avoid), ¿any ideas?

Thank you people for reading and sorry about my english :blush:


#2

Traditionally the answer is struct SerializableTimespec(pub Timespec) and implementing the traits on that. You can either implement Deref for it to, or just access the inner value with .0.


#3

Json doesn’t have a standardized time format. Therefor there’s no way to implement Serialize for Timespec without someone complaining that it’s not in the format they want it.

Thus everyone needs to implement it on their own, as @jdm showed.


#4

I actually never thought of deref ! Ty :smile:
I really didn’t want to re-implement all the traits of time in my own struct, so deref really throws me a bone !

I’ll post how I solved it (for anyone else facing same problematic):
I created a sub-module called time in my crate, and pub re-exported the crate time so I don’t have to deal with 2 different “time” things (the crate, and the module with my container struct). I rather expand on time adding another type that’s serde serializable. So -more or less- it goes like this(the type it’s called other than MyTime):

//! Este módulo wrappea `Timespec` de manera que sea serializable y de-serializable

use time;
use serde;
use std::ops::Deref;

/// Re-export del crate `time` para "expandirlo"
pub use time::*;

/// Este es un contenedor de un `time::Timespec`
#[derive(Debug)]
pub struct MyTime
{
    pub internal_time:  time::Timespec,
}

/// Permitir hacer un Deref a un `time::Timespec`
impl Deref for MyTime {
    type Target = time::Timespec;

    fn deref(&self) -> &time::Timespec{
        &self.internal_time
    }
}

impl MyTime {
        /// Crear `MyTime` a partir de un `time::TImespec`
        pub fn new(enclosed_time: time::Timespec) -> MyTime
        {
            MyTime{internal_time: enclosed_time}
        }
}

/// Implemento serialización `MyTime`
impl serde::Serialize for MyTime {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: serde::Serializer,
    {
        // Parseamos Timespec -> String
        let parsed_time = time::strftime("%d/%m/%Y %X%z", &time::at(self.internal_time))
                               .ok()
                               .expect("strftime failed hard on MyTime"); // shouldn't happen

        serializer.visit_str(&parsed_time)
    }
}

/// Implemento deserialización `MyTime`
impl serde::Deserialize for MyTime  {
    fn deserialize<D>(deserializer: &mut D) -> Result<MyTime, D::Error>
        where D: serde::Deserializer,
    {
        deserializer.visit(TimeVisitor)
    }
}

/// Visitor para "recorrer" los datos
struct TimeVisitor;

/// Implemento un visitor para "recorrer" los datos
impl serde::de::Visitor for TimeVisitor {
    type Value = MyTime;

    fn visit_string<E>(&mut self, str_data: String) -> Result<MyTime, E>
        where E: serde::de::Error,
    {
        // Vamos con un strptime() con nuestro formato y vemos que paso
        match time::strptime(&str_data, "%d/%m/%Y %X%z")
        {
            // Todo ok, convertimos al Timespec
            Ok(parsed_time) => Ok(MyTime::new(parsed_time.to_timespec())),
            
            // Hubo un problema, formatearlo y pasarlo a serde
            Err(parse_error) => Err(serde::de::Error::syntax(&format!(
                "time parser error: {}", parse_error)))
        }
    }
}

And now I’m eager to see if I can serialize my structs through bincode and cache that with redis and back *.*