# Overview - [📦 crates.io](https://crates.io/crates/serdapt) - [📖 Documentation](https://docs.rs/serdapt) - [⚖ 0BSD license](https://spdx.org/licenses/0BSD.html) Tools to build composable adapters for `#[serde(with = ...)]`. `serde` allows customizing how fields are serialized when deriving `Serialize` and `Deserialize` thanks to the `#[serde(with = "path")]` attribute. With such an attribute, `path::serialize` and `path::deserialize` are the functions used for serialization. By using a type for `path`, composable serialization adapters can be defined, e.g. to customize how items in a container are serialized. These adapters can also simplify implementing `Serialize` and `Deserialize`. # Apply adapter An adapter is applied by specifying the adapter path in `#[serde(with = "...")]`. The path needs to be suitable as a prefix for functions, i.e. `path::serialize` and `path::deserialize`. This means the turbofish is needed for generic adapters, e.g. `Outer::`. ## Example ```rust use serde::{Deserialize, Serialize}; #[derive(Debug, Deserialize, PartialEq, Serialize)] struct Foo { #[serde(with = "serdapt::Seq::")] xs: Vec, } let foo = Foo { xs: vec![3, 4] }; let v = serde_json::to_value(&foo).unwrap(); assert_eq!(v, serde_json::json!({ "xs": ["3", "4"] })); assert_eq!(serde_json::from_value::(v).unwrap(), foo); ``` # Define serialization adapter 1. Define a type to represent the new adapter. 1. Implement [`SerializeWith`] and [`DeserializeWith`] for this type. This allows adapter composability. 1. Define `serialize` and `deserialize` inherent functions for this type, delegating to [`SerializeWith`] and [`DeserializeWith`] respectively. These are the functions the serde-generated code calls. ## Simple adapter example ```rust use serdapt::{DeserializeWith, SerializeWith}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::json; #[derive(Deserialize, Serialize)] struct Point { x: i32, y: i32, } struct Coords; impl Coords { fn serialize(value: &T, serializer: S) -> Result where T: ?Sized, S: Serializer, Self: SerializeWith, { Self::serialize_with(value, serializer) } fn deserialize<'de, T, D>(deserializer: D) -> Result where D: Deserializer<'de>, Self: DeserializeWith<'de, T>, { Self::deserialize_with(deserializer) } } impl SerializeWith<(i32, i32)> for Coords { fn serialize_with(&(x, y): &(i32, i32), serializer: S) -> Result where S: Serializer, { Serialize::serialize(&Point { x, y }, serializer) } } impl<'de> DeserializeWith<'de, (i32, i32)> for Coords { fn deserialize_with(deserializer: D) -> Result<(i32, i32), D::Error> where D: Deserializer<'de>, { let Point { x, y } = Deserialize::deserialize(deserializer)?; Ok((x, y)) } } #[derive(Debug, Deserialize, PartialEq, Serialize)] struct Shape(#[serde(with = "serdapt::Seq::")] Vec<(i32, i32)>); let original = Shape(vec![(1, 2), (3, 4)]); let serialized = serde_json::to_value(&original).unwrap(); assert_eq!(serialized, json!([{ "x": 1, "y": 2 }, { "x": 3, "y": 4 }])); let deserialized = serde_json::from_value::(serialized).unwrap(); assert_eq!(deserialized, original); ``` ## Generic adapter example ```rust use core::marker::PhantomData; use serdapt::{DeserializeWith, SerializeWith, WithEncoding}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::json; #[derive(Debug, Deserialize, PartialEq, Serialize)] struct Point { x: T, y: T, } struct Coords(PhantomData); impl Coords { fn serialize(value: &T, serializer: S) -> Result where T: ?Sized, S: Serializer, Self: SerializeWith, { Self::serialize_with(value, serializer) } fn deserialize<'de, T, D>(deserializer: D) -> Result where D: Deserializer<'de>, Self: DeserializeWith<'de, T>, { Self::deserialize_with(deserializer) } } impl SerializeWith> for Coords where F: SerializeWith, { fn serialize_with(Point { x, y }: &Point, serializer: S) -> Result where S: Serializer, { let p: Point> = Point { x: x.into(), y: y.into() }; Serialize::serialize(&p, serializer) } } impl<'de, F, T> DeserializeWith<'de, Point> for Coords where F: DeserializeWith<'de, T>, { fn deserialize_with(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { let p: Point> = Deserialize::deserialize(deserializer)?; Ok(Point { x: p.x.into_inner(), y: p.y.into_inner(), }) } } #[derive(Debug, Deserialize, PartialEq, Serialize)] struct Shape( #[serde(with = "serdapt::Seq::>")] Vec>, ); let original = Shape(vec![Point { x: 1, y: 2 }, Point { x: 3, y: 4 }]); let serialized = serde_json::to_value(&original).unwrap(); assert_eq!(serialized, json!([{ "x": "1", "y": "2" }, { "x": "3", "y": "4" }])); let deserialized = serde_json::from_value::(serialized).unwrap(); assert_eq!(deserialized, original); ``` # Related project [`serde_with`](https://crates.io/crates/serde_with) allows the same composability with the help of an additional proc-macro, though it is also possible to use `#[serde(with = ...)]` directly. Some key differences are: - `serdapt` is simpler and does not need any additional proc-macro, giving up on any ergonomics such a macro provides. - It avoids a macro ordering issue that can lead to generated serialization code not using the requested adapter despite a sucessful compilation. - It works seamlessly with conditional compilation. - It is limited to supporting types in the standard library, with support for third-party types delegated to other crates, which solves dependency issues. # Contribute All contributions shall be licensed under the [0BSD license](https://spdx.org/licenses/0BSD.html).