Implement deserialization support for flatten

This commit is contained in:
Armin Ronacher 2018-03-16 01:23:57 +01:00
parent 112dfd7428
commit ebf80ac965
2 changed files with 118 additions and 4 deletions

View File

@ -8,7 +8,7 @@
use lib::*;
use de::{Deserialize, DeserializeSeed, Deserializer, Error, IntoDeserializer, Visitor};
use de::{Deserialize, DeserializeSeed, Deserializer, Error, IntoDeserializer, Visitor, MapAccess};
#[cfg(any(feature = "std", feature = "alloc"))]
use de::Unexpected;
@ -2062,3 +2062,117 @@ where
T::deserialize_in_place(deserializer, self.0)
}
}
#[cfg(any(feature = "std", feature = "alloc"))]
pub struct FlatMapDeserializer<'a, 'de: 'a, E>(
pub &'a mut Vec<Option<(String, Content<'de>)>>,
pub PhantomData<E>
);
#[cfg(any(feature = "std", feature = "alloc"))]
impl<'a, 'de, E> Deserializer<'de> for FlatMapDeserializer<'a, 'de, E>
where E: Error
{
type Error = E;
fn deserialize_any<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
Err(Error::custom("can only deserialize structs and maps in flatten mode"))
}
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_map(FlatMapAccess::new(self.0.iter_mut(), None))
}
fn deserialize_struct<V>(
self,
_: &'static str,
fields: &'static [&'static str],
visitor: V
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_map(FlatMapAccess::new(self.0.iter_mut(), Some(fields)))
}
forward_to_deserialize_any! {
bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes
byte_buf option unit unit_struct newtype_struct seq tuple
tuple_struct enum identifier ignored_any
}
}
#[cfg(any(feature = "std", feature = "alloc"))]
pub struct FlatMapAccess<'a, 'de: 'a, E> {
iter: slice::IterMut<'a, Option<(String, Content<'de>)>>,
pending_content: Option<Content<'de>>,
fields: Option<&'static [&'static str]>,
_marker: PhantomData<E>,
}
impl<'a, 'de, E> FlatMapAccess<'a, 'de, E> {
fn new(
iter: slice::IterMut<'a, Option<(String, Content<'de>)>>,
fields: Option<&'static [&'static str]>
) -> FlatMapAccess<'a, 'de, E> {
FlatMapAccess {
iter: iter,
pending_content: None,
fields: fields,
_marker: PhantomData,
}
}
}
#[cfg(any(feature = "std", feature = "alloc"))]
impl<'a, 'de, E> MapAccess<'de> for FlatMapAccess<'a, 'de, E>
where E: Error
{
type Error = E;
fn next_key_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
where
T: DeserializeSeed<'de>,
{
loop {
let item = match self.iter.next() {
Some(item) => {
if item.is_some() {
item
} else {
continue;
}
}
None => return Ok(None)
};
if match self.fields {
None => false,
Some(fields) if fields.contains(&item.as_ref().unwrap().0.as_str()) => false,
_ => true
} {
continue;
}
let (key, content) = item.take().unwrap();
self.pending_content = Some(content);
return seed.deserialize(ContentDeserializer::new(Content::String(key))).map(Some);
}
}
fn next_value_seed<T>(&mut self, seed: T) -> Result<T::Value, Self::Error>
where
T: DeserializeSeed<'de>,
{
match self.pending_content.take() {
Some(value) => seed.deserialize(ContentDeserializer::new(value)),
None => Err(Error::custom("value is missing")),
}
}
}

View File

@ -2026,7 +2026,7 @@ fn deserialize_map(
// Collect contents for flatten fields into a buffer
let let_collect = if cattrs.has_flatten() {
Some(quote! {
let mut __collect = Vec::<(String, _serde::private::de::Content)>::new();
let mut __collect = Vec::<Option<(String, _serde::private::de::Content)>>::new();
})
} else {
None
@ -2070,7 +2070,7 @@ fn deserialize_map(
let ignored_arm = if cattrs.has_flatten() {
Some(quote! {
__Field::__other(__name) => {
__collect.push((__name, try!(_serde::de::MapAccess::next_value(&mut __map))));
__collect.push(Some((__name, try!(_serde::de::MapAccess::next_value(&mut __map)))));
}
})
} else if cattrs.deny_unknown_fields() {
@ -2130,7 +2130,7 @@ fn deserialize_map(
let collected_deny_unknown_fields = if cattrs.has_flatten() && cattrs.deny_unknown_fields() {
Some(quote! {
if let Some((__key, _)) = __collect.into_iter().next() {
if let Some(Some((__key, _))) = __collect.into_iter().filter(|x| x.is_some()).next() {
return _serde::export::Err(
_serde::de::Error::unknown_field_in_flattened_structure(&__key));
}