Serde: Document how to serialize using Display, deserialize using FromStr

Created on 15 Jun 2018  路  3Comments  路  Source: serde-rs/serde

Often a type implements Display and FromStr but not Serialize and Deserialize.

The module from https://github.com/serde-rs/json/issues/329#issuecomment-305608405 should work.

#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate serde_json;

use std::fmt::{self, Display};
use std::str::FromStr;

#[derive(Debug)]
struct X;

impl Display for X {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("X")
    }
}

impl FromStr for X {
    type Err = &'static str;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "X" => Ok(X),
            _ => Err("was not X"),
        }
    }
}

#[derive(Serialize, Deserialize, Debug)]
struct S {
    #[serde(with = "string")]
    x: X
}

mod string {
    use std::fmt::Display;
    use std::str::FromStr;

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

    pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
    where
        T: Display,
        S: Serializer
    {
        serializer.collect_str(value)
    }

    pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
    where
        T: FromStr,
        T::Err: Display,
        D: Deserializer<'de>
    {
        String::deserialize(deserializer)?.parse().map_err(de::Error::custom)
    }
}

fn main() {
    let j = r#" { "x": "X" } "#;

    let s: S = serde_json::from_str(j).unwrap();
    println!("{:#?}", s);

    println!("{}", serde_json::to_string_pretty(&s).unwrap());
}
docs

Most helpful comment

I came here to suggest a simple way to do exactly this. I think it would be great if serde would provide a way to do that with some kind of derive-like thing.

However, in the case of deserialization, we should try avoid the unnecessary allocation of String. Since FromStr takes a &str, it should be possible to use a str directly using a visitor implementation.

All 3 comments

I came here to suggest a simple way to do exactly this. I think it would be great if serde would provide a way to do that with some kind of derive-like thing.

However, in the case of deserialization, we should try avoid the unnecessary allocation of String. Since FromStr takes a &str, it should be possible to use a str directly using a visitor implementation.

An alternative I cooked up from the original description: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=2b642a9a0b8747fe59e9190cc273810c or with some fixed typos and added deref and derefmut in rs-ipfs/rust-ipfs/.../http/src/v0/support/serdesupport.rs which might be of interest to someone. Apologies if this wrapper is already in serde, but let me know please? :)

Wouldn't it be possible to do something like this:

#[derive(Serialize, Deserialize)]
#[serde(stringify)]
pub MyType { ... }

impl fmt::Display for MyType { ... }

impl str::FromStr for MyType { ... }
Was this page helpful?
0 / 5 - 0 ratings