urlencoded: (de)serialize nested structs as JSON

This commit is contained in:
Ragotzy.devin 2020-04-29 03:58:30 -04:00 committed by GitHub
parent 053d2e94f6
commit 7972453e91
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 230 additions and 6 deletions

View File

@ -233,8 +233,12 @@ impl<'de> de::Deserializer<'de> for Part<'de> {
where where
V: de::Visitor<'de>, V: de::Visitor<'de>,
{ {
if self.0 == "null" {
visitor.visit_none()
} else {
visitor.visit_some(self) visitor.visit_some(self)
} }
}
fn deserialize_enum<V>( fn deserialize_enum<V>(
self, self,
@ -269,6 +273,29 @@ impl<'de> de::Deserializer<'de> for Part<'de> {
)) ))
} }
fn deserialize_struct<V>(
self,
name: &'static str,
fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
let pairs = self
.1
.ok_or_else(|| Error::custom("percent decoding may have failed"))?;
let raw_json = pairs
.get(0)
.ok_or_else(|| Error::custom("no value found for nested struct"))?;
let mut de =
serde_json::de::Deserializer::from_reader(raw_json.as_bytes());
de.deserialize_struct(name, fields, visitor)
.map_err(|e| Error::custom(e.to_string()))
}
serde::forward_to_deserialize_any! { serde::forward_to_deserialize_any! {
char char
str str
@ -278,7 +305,6 @@ impl<'de> de::Deserializer<'de> for Part<'de> {
byte_buf byte_buf
unit_struct unit_struct
tuple_struct tuple_struct
struct
identifier identifier
tuple tuple
ignored_any ignored_any

View File

@ -32,6 +32,8 @@ pub struct PairSerializer<'input, 'target, Target: 'target + UrlEncodedTarget> {
state: PairState, state: PairState,
key: Option<&'target Cow<'target, str>>, key: Option<&'target Cow<'target, str>>,
count: &'target mut usize, count: &'target mut usize,
len: usize,
json_buf: String,
} }
impl<'input, 'target, Target> PairSerializer<'input, 'target, Target> impl<'input, 'target, Target> PairSerializer<'input, 'target, Target>
@ -48,6 +50,8 @@ where
state: PairState::WaitingForKey, state: PairState::WaitingForKey,
key, key,
count, count,
len: 0,
json_buf: String::new(),
} }
} }
} }
@ -64,7 +68,7 @@ where
type SerializeTupleStruct = ser::Impossible<(), Error>; type SerializeTupleStruct = ser::Impossible<(), Error>;
type SerializeTupleVariant = ser::Impossible<(), Error>; type SerializeTupleVariant = ser::Impossible<(), Error>;
type SerializeMap = ser::Impossible<(), Error>; type SerializeMap = ser::Impossible<(), Error>;
type SerializeStruct = ser::Impossible<(), Error>; type SerializeStruct = Self;
type SerializeStructVariant = ser::Impossible<(), Error>; type SerializeStructVariant = ser::Impossible<(), Error>;
serialize_pair! { serialize_pair! {
@ -177,11 +181,12 @@ where
} }
fn serialize_struct( fn serialize_struct(
self, mut self,
_name: &'static str, _name: &'static str,
_len: usize, len: usize,
) -> Result<Self::SerializeStruct> { ) -> Result<Self::SerializeStruct> {
Err(Error::unsupported_pair()) self.len = len;
Ok(self)
} }
fn serialize_struct_variant( fn serialize_struct_variant(
@ -195,6 +200,51 @@ where
} }
} }
impl<'input, 'target, Target> ser::SerializeStruct
for PairSerializer<'input, 'target, Target>
where
Target: 'target + UrlEncodedTarget,
{
type Ok = ();
type Error = Error;
fn serialize_field<T: ?Sized>(
&mut self,
key: &'static str,
value: &T,
) -> Result<()>
where
T: ser::Serialize,
{
*self.count += 1;
if self.json_buf.is_empty() {
self.json_buf.push_str("{");
}
let json_blob = serde_json::to_string(value)
.map_err(|e| Error::Custom(e.to_string().into()))?;
if *self.count == self.len {
self.json_buf
.push_str(&format!("\"{}\":{}", key, json_blob));
} else {
self.json_buf
.push_str(&format!("\"{}\":{},", key, json_blob));
}
Ok(())
}
fn end(mut self) -> Result<Self::Ok> {
use serde::ser::Serialize;
self.json_buf.push_str("}");
self.json_buf.serialize(PairSerializer::new(
self.urlencoder,
self.key,
&mut self.count,
))
}
}
impl<'input, 'target, Target> ser::SerializeSeq impl<'input, 'target, Target> ser::SerializeSeq
for PairSerializer<'input, 'target, Target> for PairSerializer<'input, 'target, Target>
where where

View File

@ -1,5 +1,6 @@
use ruma_serde::urlencoded; use ruma_serde::urlencoded;
use serde::Deserialize; use serde::Deserialize;
use url::form_urlencoded::Serializer as Encoder;
#[derive(Deserialize, Debug, PartialEq)] #[derive(Deserialize, Debug, PartialEq)]
struct NewType<T>(T); struct NewType<T>(T);
@ -183,3 +184,81 @@ fn deserialize_struct_unit_enum() {
assert_eq!(urlencoded::from_str("item=A&item=B&item=C"), Ok(result)); assert_eq!(urlencoded::from_str("item=A&item=B&item=C"), Ok(result));
} }
#[derive(Debug, Deserialize, PartialEq)]
struct Nested<T> {
item: T,
}
#[derive(Debug, Deserialize, PartialEq)]
struct Inner {
c: String,
a: usize,
b: String,
}
#[derive(Debug, Deserialize, PartialEq)]
struct InnerList<T> {
list: Vec<T>,
}
#[test]
fn deserialize_nested() {
let mut encoder = Encoder::new(String::new());
let nested = Nested {
item: Inner {
c: "hello".into(),
a: 10,
b: "bye".into(),
},
};
assert_eq!(
nested,
urlencoded::from_str::<Nested<Inner>>(
&encoder
.append_pair("item", r#"{"c":"hello","a":10,"b":"bye"}"#)
.finish(),
)
.unwrap()
);
}
#[test]
fn deserialize_nested_list() {
let mut encoder = Encoder::new(String::new());
let nested = Nested {
item: InnerList {
list: vec![1, 2, 3],
},
};
assert_eq!(
nested,
urlencoded::from_str::<Nested<InnerList<u8>>>(
&encoder.append_pair("item", r#"{"list":[1,2,3]}"#).finish(),
)
.unwrap()
);
}
#[test]
fn deserialize_nested_list_option() {
let mut encoder = Encoder::new(String::new());
let nested = Nested {
item: InnerList {
list: vec![Some(1), Some(2), None],
},
};
assert_eq!(
nested,
urlencoded::from_str::<Nested<InnerList<Option<u8>>>>(
&encoder
.append_pair("item", r#"{"list":[1,2,null]}"#)
.finish(),
)
.unwrap()
);
}

View File

@ -1,5 +1,6 @@
use ruma_serde::urlencoded; use ruma_serde::urlencoded;
use serde::Serialize; use serde::Serialize;
use url::form_urlencoded::Serializer as Encoder;
#[derive(Serialize)] #[derive(Serialize)]
struct NewType<T>(T); struct NewType<T>(T);
@ -190,3 +191,71 @@ fn serialize_map() {
let encoded = urlencoded::to_string(s).unwrap(); let encoded = urlencoded::to_string(s).unwrap();
assert_eq!("hello=world&matrix=ruma&seri=alize", encoded); assert_eq!("hello=world&matrix=ruma&seri=alize", encoded);
} }
#[derive(Serialize)]
struct Nested<T> {
item: T,
}
#[derive(Serialize)]
struct Inner {
c: String,
a: usize,
b: String,
}
#[derive(Debug, Serialize, PartialEq)]
struct InnerList<T> {
list: Vec<T>,
}
#[test]
fn serialize_nested() {
let mut encoder = Encoder::new(String::new());
let s = Nested {
item: Inner {
c: "hello".into(),
a: 10,
b: "bye".into(),
},
};
assert_eq!(
encoder
.append_pair("item", r#"{"c":"hello","a":10,"b":"bye"}"#)
.finish(),
urlencoded::to_string(s).unwrap()
);
}
#[test]
fn serialize_nested_list() {
let mut encoder = Encoder::new(String::new());
let s = Nested {
item: InnerList {
list: vec![1, 2, 3],
},
};
assert_eq!(
encoder.append_pair("item", r#"{"list":[1,2,3]}"#).finish(),
urlencoded::to_string(s).unwrap()
);
}
#[test]
fn serialize_nested_list_option() {
let mut encoder = Encoder::new(String::new());
let s = Nested {
item: InnerList {
list: vec![Some(1), Some(2), None],
},
};
assert_eq!(
encoder
.append_pair("item", r#"{"list":[1,2,null]}"#)
.finish(),
urlencoded::to_string(s).unwrap()
);
}