Deriving De/Serialize for type in a different crate

Rust's orphan rule requires that either the trait or the type for which you are implementing the trait must be defined in the same crate as the impl, so it is not possible to implement Serialize and Deserialize for a type in a different crate directly.

- use serde::Serialize;
- use other_crate::Duration;
-
- // Not allowed by orphan rule.
- impl Serialize for Duration {
-     /* ... */
- }

To work around this, Serde provides a way of deriving Serialize and Deserialize implementations for types in other people's crates. The only catch is that you have to provide a definition of the type for Serde's derive to process. At compile time, Serde will check that all the fields in the definition you provided match the fields in the remote type.

// Pretend that this is somebody else's crate, not a module.
mod other_crate {
    // Neither Serde nor the other crate provides Serialize and Deserialize
    // impls for this struct.
    pub struct Duration {
        pub secs: i64,
        pub nanos: i32,
    }
}

////////////////////////////////////////////////////////////////////////////////

#[macro_use]
extern crate serde_derive;

extern crate serde;

use other_crate::Duration;

// Serde calls this the definition of the remote type. It is just a copy of the
// remote data structure. The `remote` attribute gives the path to the actual
// type we intend to derive code for.
#[derive(Serialize, Deserialize)]
#[serde(remote = "Duration")]
struct DurationDef {
    secs: i64,
    nanos: i32,
}

// Now the remote type can be used almost like it had its own Serialize and
// Deserialize impls all along. The `with` attribute gives the path to the
// definition for the remote type. Note that the real type of the field is the
// remote type, not the definition type.
#[derive(Serialize, Deserialize)]
struct Process {
    command_line: String,

    #[serde(with = "DurationDef")]
    wall_time: Duration,
}

If the remote type is a struct with all public fields or an enum, that's all there is to it. If the remote type is a struct with one or more private fields, getters must be provided for the private fields and a conversion must be provided to construct the remote type.

// Pretend that this is somebody else's crate, not a module.
mod other_crate {
    // Neither Serde nor the other crate provides Serialize and Deserialize
    // impls for this struct. Oh, and the fields are private.
    pub struct Duration {
        secs: i64,
        nanos: i32,
    }

    impl Duration {
        pub fn new(secs: i64, nanos: i32) -> Self {
            Duration { secs: secs, nanos: nanos }
        }

        pub fn seconds(&self) -> i64 {
            self.secs
        }

        pub fn subsec_nanos(&self) -> i32 {
            self.nanos
        }
    }
}

////////////////////////////////////////////////////////////////////////////////

#[macro_use]
extern crate serde_derive;

extern crate serde;

use other_crate::Duration;

// Provide getters for every private field of the remote struct. The getter must
// return either `T` or `&T` where `T` is the type of the field.
#[derive(Serialize, Deserialize)]
#[serde(remote = "Duration")]
struct DurationDef {
    #[serde(getter = "Duration::seconds")]
    secs: i64,
    #[serde(getter = "Duration::subsec_nanos")]
    nanos: i32,
}

// Provide a conversion to construct the remote type.
impl From<DurationDef> for Duration {
    fn from(def: DurationDef) -> Duration {
        Duration::new(def.secs, def.nanos)
    }
}

#[derive(Serialize, Deserialize)]
struct Process {
    command_line: String,

    #[serde(with = "DurationDef")]
    wall_time: Duration,
}

Invoking the remote impl directly

As shown above, the remote impl is intended to be invoked through a #[serde(with = "...")] attribute on a field of some other struct.

Invoking the remote impl directly, such as if this is the top-level type being serialized or deserialized, is somewhat more complicated because of the orphan rules as mentioned. The code ultimately generated by these remote derives are not Serialize and Deserialize impls but associated functions with the same signature.

// Technically this derive does not produce a Deserialize impl for Duration, nor
// a Deserialize impl for DurationDef.
//
// Instead it produces a deserialization method DurationDef::deserialize whose
// return type is Duration. The method has the same signature as a Deserialize
// impl for Duration would have but is not a Deserialize impl.
#[derive(Deserialize)]
#[serde(remote = "Duration")]
struct DurationDef {
    secs: i64,
    nanos: i32,
}

Knowing this, the generated method can be invoked directly by passing a Deserializer implementation.

let mut de = serde_json::Deserializer::from_str(j);
let dur = DurationDef::deserialize(&mut de)?;

// `dur` has type Duration

Alternatively we can write a top-level newtype wrapper as a private helper for deserializing the remote type.

#[derive(Deserialize)]
struct Helper(#[serde(with = "DurationDef")] Duration);

let dur = serde_json::from_str(j).map(|Helper(dur)| dur)?;

// `dur` has type Duration