From b69c42eb9b9f055e74353c9c149074616eff181f Mon Sep 17 00:00:00 2001 From: Sam Scott Date: Wed, 23 Jan 2019 11:13:18 -0500 Subject: Add documentation and workaroud for `#[serde(flatten)]` usage. --- src/lib.rs | 48 ++++++++++++++++++++++++++++++++++++++ tests/test_deserialize.rs | 59 +++++++++++++++++++++++++++++++++++++++++++++++ tests/test_serialize.rs | 21 +++++++++++++++++ 3 files changed, 128 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 15e4036..1c3d33e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -97,7 +97,55 @@ //! using keys with square brackets in them, or unexpected things can //! happen. //! +//! ## Flatten workaround +//! +//! A current [known limitation](https://github.com/serde-rs/serde/issues/1183) +//! in `serde` is deserializing `#[serde(flatten)]` structs for formats which +//! are not self-describing. This includes query strings: `12` can be an integer +//! or a string, for example. +//! +//! We suggest the following workaround: +//! +//! ``` +//! extern crate serde; +//! #[macro_use] +//! extern crate serde_derive; +//! extern crate serde_qs as qs; +//! +//! use serde::de::Error; //! +//! fn from_str<'de, D, S>(deserializer: D) -> Result +//! where D: serde::Deserializer<'de>, +//! S: std::str::FromStr +//! { +//! let s = <&str as serde::Deserialize>::deserialize(deserializer)?; +//! S::from_str(&s).map_err(|_| D::Error::custom("could not parse string")) +//! } +//! +//! #[derive(Deserialize, Serialize, Debug, PartialEq)] +//! struct Query { +//! a: u8, +//! #[serde(flatten)] +//! common: CommonParams, +//! } +//! +//! #[derive(Deserialize, Serialize, Debug, PartialEq)] +//! struct CommonParams { +//! #[serde(deserialize_with="from_str")] +//! limit: u64, +//! #[serde(deserialize_with="from_str")] +//! offset: u64, +//! #[serde(deserialize_with="from_str")] +//! remaining: bool, +//! } +//! +//! fn main() { +//! let params = "a=1&limit=100&offset=50&remaining=true"; +//! let query = Query { a: 1, common: CommonParams { limit: 100, offset: 50, remaining: true } }; +//! let rec_query: Result = qs::from_str(params); +//! assert_eq!(rec_query.unwrap(), query); +//! } +//! ``` #![allow( )] diff --git a/tests/test_deserialize.rs b/tests/test_deserialize.rs index 946a8f3..81dc954 100644 --- a/tests/test_deserialize.rs +++ b/tests/test_deserialize.rs @@ -1,3 +1,4 @@ +extern crate serde; #[macro_use] extern crate serde_derive; extern crate serde_qs as qs; @@ -479,3 +480,61 @@ fn strict_mode() { fn square_brackets_in_values() { map_test!("foo=%5BHello%5D", "foo"["[Hello]"]); } + +#[test] +#[ignore] +fn deserialize_flatten() { + #[derive(Deserialize,Serialize,Debug, PartialEq)] + struct Query { + a: u8, + #[serde(flatten)] + common: CommonParams, + } + + #[derive(Deserialize,Serialize,Debug, PartialEq)] + struct CommonParams { + limit: u64, + offset: u64, + remaining: bool, + } + + let params = "a=1&limit=100&offset=50&remaining=true"; + let query = Query { a: 1, common: CommonParams { limit: 100, offset: 50, remaining: true } }; + let rec_query: Result = qs::from_str(params); + assert_eq!(rec_query.unwrap(), query); +} + +#[test] +fn deserialize_flatten_workaround() { + #[derive(Deserialize,Serialize,Debug, PartialEq)] + struct Query { + a: u8, + #[serde(flatten)] + common: CommonParams, + } + + #[derive(Deserialize,Serialize,Debug, PartialEq)] + struct CommonParams { + #[serde(deserialize_with="from_str")] + limit: u64, + #[serde(deserialize_with="from_str")] + offset: u64, + #[serde(deserialize_with="from_str")] + remaining: bool, + } + + let params = "a=1&limit=100&offset=50&remaining=true"; + let query = Query { a: 1, common: CommonParams { limit: 100, offset: 50, remaining: true } }; + let rec_query: Result = qs::from_str(params); + assert_eq!(rec_query.unwrap(), query); +} + +use serde::de::Error; + +fn from_str<'de, D, S>(deserializer: D) -> Result + where D: serde::Deserializer<'de>, + S: std::str::FromStr +{ + let s = <&str as serde::Deserialize>::deserialize(deserializer)?; + S::from_str(&s).map_err(|_| D::Error::custom("could not parse string")) +} \ No newline at end of file diff --git a/tests/test_serialize.rs b/tests/test_serialize.rs index 80e2d7c..5a918ab 100644 --- a/tests/test_serialize.rs +++ b/tests/test_serialize.rs @@ -92,3 +92,24 @@ fn serialize_enum() { let rec_params = qs::to_string(&query).unwrap(); assert_eq!(rec_params, params); } + +#[test] +fn serialize_flatten() { + #[derive(Deserialize,Serialize,Debug, PartialEq)] + struct Query { + a: u8, + #[serde(flatten)] + common: CommonParams, + } + + #[derive(Deserialize,Serialize,Debug, PartialEq)] + struct CommonParams { + limit: u64, + offset: u64, + } + + let params = "a=1&limit=100&offset=50"; + let query = Query { a: 1, common: CommonParams { limit: 100, offset: 50 } }; + let rec_params = qs::to_string(&query).unwrap(); + assert_eq!(rec_params, params); +} -- cgit v1.2.3