Crate derive_serialize_into [] [src]

Derive Serialize and Deserialize for wrapper types

This crate provides several custom derives that provide implementations of serde's Serialize and Deserialize traits for wrapper types, as well as Deserialize implementations that perform some validation.

Sometimes you have a single-field type

struct Contact {
    email: String,
}

which you want to serialize and deserialize as a string instead of a struct, e.g. you want its JSON representation to just be ""user@domain.com"" instead of "{ "email": "user@domain.com" }". You can derive Serialize and Deserialize implementations for that purpose, as well as Into and From implementations to convert between String and Contact.

Another example is a validated wrapper type like

struct Email(String);

that should never be instantianted with a string that doesn't represent a valid email address. If you implement Email: TryFrom<String> using the try_from crate, such that conversion fails for invalid addresses, the derived Deserialize will also fail if the string is in the wrong format.

Supported derive attributes

#[derive(IntoInner)]

On structures T with a single field of type S, this creates an impl From<&T> for &S that returns a reference to the field.

#[derive(FromInner)]

On structures T with a single field of type S, this creates an impl From<S> for T.

#[derive(SerializeInto)]

On structures T, given an impl Into<S> for &T, this creates an impl Serialize for T which first converts to S and then serializes it. If #[serialize_into(S)] is not specified and T is a structure with a single field, it uses a reference to that field's type as S.

#[derive(DeserializeFrom)]

On structures T, given an impl Into<T> for S, this creates an impl Deserialize for T which first deserializes to S and then converts to T. If #[deserialize_from(S)] is not specified and T is a structure with a single field, it uses that field's type as S.

#[derive(DeserializeTryFrom)]

On structures T, given an impl TryInto<T> for S, this creates an impl Deserialize for T which first deserializes to S and then converts to T. If #[deserialize_from(S)] is not specified and T is a structure with a single field, it uses that field's type as S. Deserialization fails if conversion from S to T fails. The TryInto implementation's error type must implement Display and will be converted to a custom deserialization error.

Example: simple wrapper types

#[macro_use]
extern crate derive_serialize_into;
extern crate serde;
extern crate serde_json;

#[derive(DeserializeFrom, FromInner, IntoInner, SerializeInto, Debug, Eq, PartialEq)]
struct Seconds(i64);

#[derive(DeserializeFrom, FromInner, IntoInner, SerializeInto, Debug, Eq, PartialEq)]
struct Days {
    number: i64,
}

fn main() {
    assert_eq!(Seconds(5), serde_json::from_str("5").unwrap());
    assert_eq!("5", &serde_json::to_string(&Seconds(5)).unwrap());
    assert!(serde_json::from_str::<Seconds>("nan").is_err());
    assert_eq!(Days { number: 5 }, serde_json::from_str("5").unwrap());
    assert_eq!("5", &serde_json::to_string(&Days { number: 5 }).unwrap());
    assert!(serde_json::from_str::<Days>("nan").is_err());
}

Example: validated email addresses

#[macro_use]
extern crate derive_serialize_into;
extern crate serde;
extern crate serde_json;
extern crate try_from;
extern crate validator;

#[derive(DeserializeTryFrom, IntoInner, SerializeInto, Debug, Eq, PartialEq)]
struct Email(String);

impl try_from::TryFrom<String> for Email {
    type Err = &'static str;

    fn try_from(raw: String) -> Result<Email, &'static str> {
        if validator::validate_email(&raw) {
            Ok(Email(raw))
        } else {
            Err("invalid email address")
        }
    }
}

fn main() {
    let email_json = r#""user@domain.com""#;
    let email = Email("user@domain.com".to_string());
    assert_eq!(email, serde_json::from_str(email_json).unwrap());
    assert_eq!(email_json, &serde_json::to_string(&email).unwrap());
    assert!(serde_json::from_str::<Email>(r#""missing_at_sign""#).is_err());
}

Example: custom TryFrom and Into

#[macro_use]
extern crate derive_serialize_into;
extern crate serde;
extern crate serde_json;
extern crate try_from;

#[derive(DeserializeTryFrom, SerializeInto, Debug, Eq, PartialEq)]
#[serialize_into(String)]
#[deserialize_from(String)]
enum Money {
    Eur(u64),
    Usd(u64),
}

impl try_from::TryFrom<String> for Money {
    type Err = &'static str;

    fn try_from(s: String) -> Result<Money, &'static str> {
        let amt_str: String = s.chars().skip(1).collect::<String>();
        let amt = amt_str.parse().map_err(|_| "invalid amount")?;
        match s.chars().next() {
            Some('€') => Ok(Money::Eur(amt)),
            Some('$') => Ok(Money::Usd(amt)),
            _ => Err("invalid currency"),
        }
    }
}

impl<'a> From<&'a Money> for String {
    fn from(money: &Money) -> String {
        match *money {
            Money::Eur(amt) => format!("€{}", amt),
            Money::Usd(amt) => format!("${}", amt),
        }
    }
}

fn main() {
     assert_eq!(Money::Eur(5), serde_json::from_str(r#""€5""#).unwrap());
     assert_eq!(r#""$5""#, &serde_json::to_string(&Money::Usd(5)).unwrap());
     assert!(serde_json::from_str::<Money>(r#""8""#).is_err());
}