serde
serde copied to clipboard
Document how to serialize using Display, deserialize using FromStr
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());
}
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 { ... }
Wouldn't it be possible to do something like this:
It would be definitely useful, as far as I know, there is no concise way to use Display and FromStr for enum
#[serde(stringify)]
That's a really cool idea. Would be even better if this was separate for serialization and deserialization.
there is no concise way to use Display and FromStr for enum
Can you elaborate please?
Can you elaborate please?
Well, there's not much to elaborate on. I only meant that we currently need a lot of code to de/serialize using FromStr and Display. IMHO that's a common need and it would be nice to have an attribute that would do the job for us.
Bump. I am in favour of creating such an attribute to automate this. Should we perhaps create a new issue? Or can this issue be edited to become an "improvement suggestion" of sorts?
As noted in https://github.com/serde-rs/serde/pull/2017#issuecomment-903260861, the serde_with crate implements this as SerializeDisplay and DeserializeFromStr.
Using that crate, the example in the original issue:
use std::{fmt::{self, Display}, 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"),
}
}
}
can implement Serialize and Deserialize by simply adding:
use serde_with::{DeserializeFromStr, SerializeDisplay};
#[derive(DeserializeFromStr, SerializeDisplay, Debug)]
struct X;
FWIW, I've been hitting this pretty regularly across many projects. And the nature of the problem is such that, in any given project, you probably only have a couple of types, so it doesn't really make sense to pull the entire serde_with. So, while this can be implemented by a third party, using that solution is annoying, and that, for me, points towards inclusion into the core serde.