diff --git a/serde/src/private/de.rs b/serde/src/private/de.rs index 3c56e2f9..8fdf6c36 100644 --- a/serde/src/private/de.rs +++ b/serde/src/private/de.rs @@ -2723,7 +2723,7 @@ where where V: Visitor<'de>, { - visitor.visit_map(FlatMapAccess::new(self.0.iter_mut(), None)) + visitor.visit_map(FlatMapAccess::new(self.0.iter())) } fn deserialize_struct<V>( @@ -2735,7 +2735,7 @@ where where V: Visitor<'de>, { - visitor.visit_map(FlatMapAccess::new(self.0.iter_mut(), Some(fields))) + visitor.visit_map(FlatStructAccess::new(self.0.iter_mut(), fields)) } fn deserialize_newtype_struct<V>(self, _name: &str, visitor: V) -> Result<V::Value, Self::Error> @@ -2784,22 +2784,19 @@ where #[cfg(any(feature = "std", feature = "alloc"))] pub struct FlatMapAccess<'a, 'de: 'a, E> { - iter: slice::IterMut<'a, Option<(Content<'de>, Content<'de>)>>, - pending_content: Option<Content<'de>>, - fields: Option<&'static [&'static str]>, + iter: slice::Iter<'a, Option<(Content<'de>, Content<'de>)>>, + pending_content: Option<&'a Content<'de>>, _marker: PhantomData<E>, } #[cfg(any(feature = "std", feature = "alloc"))] impl<'a, 'de, E> FlatMapAccess<'a, 'de, E> { fn new( - iter: slice::IterMut<'a, Option<(Content<'de>, Content<'de>)>>, - fields: Option<&'static [&'static str]>, + iter: slice::Iter<'a, Option<(Content<'de>, Content<'de>)>>, ) -> FlatMapAccess<'a, 'de, E> { FlatMapAccess { iter: iter, pending_content: None, - fields: fields, _marker: PhantomData, } } @@ -2812,6 +2809,61 @@ where { type Error = E; + fn next_key_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error> + where + T: DeserializeSeed<'de>, + { + while let Some(item) = self.iter.next() { + // Items in the vector are nulled out when used by a struct. + if let Some((ref key, ref content)) = *item { + self.pending_content = Some(content); + return seed.deserialize(ContentRefDeserializer::new(key)).map(Some); + } + } + Ok(None) + } + + 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(ContentRefDeserializer::new(value)), + None => Err(Error::custom("value is missing")), + } + } +} + +#[cfg(any(feature = "std", feature = "alloc"))] +pub struct FlatStructAccess<'a, 'de: 'a, E> { + iter: slice::IterMut<'a, Option<(Content<'de>, Content<'de>)>>, + pending_content: Option<Content<'de>>, + fields: &'static [&'static str], + _marker: PhantomData<E>, +} + +#[cfg(any(feature = "std", feature = "alloc"))] +impl<'a, 'de, E> FlatStructAccess<'a, 'de, E> { + fn new( + iter: slice::IterMut<'a, Option<(Content<'de>, Content<'de>)>>, + fields: &'static [&'static str], + ) -> FlatStructAccess<'a, 'de, E> { + FlatStructAccess { + iter: iter, + pending_content: None, + fields: fields, + _marker: PhantomData, + } + } +} + +#[cfg(any(feature = "std", feature = "alloc"))] +impl<'a, 'de, E> MapAccess<'de> for FlatStructAccess<'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>, @@ -2822,13 +2874,7 @@ where // about. In case we do not know which fields we want, we take them all. let use_item = match *item { None => false, - Some((ref c, _)) => c.as_str().map_or(self.fields.is_none(), |key| { - match self.fields { - None => true, - Some(fields) if fields.contains(&key) => true, - _ => false, - } - }), + Some((ref c, _)) => c.as_str().map_or(false, |key| self.fields.contains(&key)), }; if use_item { diff --git a/test_suite/tests/test_annotations.rs b/test_suite/tests/test_annotations.rs index aea692cb..bc45f37c 100644 --- a/test_suite/tests/test_annotations.rs +++ b/test_suite/tests/test_annotations.rs @@ -14,7 +14,7 @@ extern crate serde_derive; extern crate serde; use self::serde::de::{self, Unexpected}; use self::serde::{Deserialize, Deserializer, Serialize, Serializer}; -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; use std::marker::PhantomData; extern crate serde_test; @@ -1683,6 +1683,49 @@ fn test_complex_flatten() { ); } +#[test] +fn test_flatten_map_twice() { + #[derive(Debug, PartialEq, Deserialize)] + struct Outer { + #[serde(flatten)] + first: BTreeMap<String, String>, + #[serde(flatten)] + between: Inner, + #[serde(flatten)] + second: BTreeMap<String, String>, + } + + #[derive(Debug, PartialEq, Deserialize)] + struct Inner { + y: String, + } + + assert_de_tokens( + &Outer { + first: { + let mut first = BTreeMap::new(); + first.insert("x".to_owned(), "X".to_owned()); + first.insert("y".to_owned(), "Y".to_owned()); + first + }, + between: Inner { y: "Y".to_owned() }, + second: { + let mut second = BTreeMap::new(); + second.insert("x".to_owned(), "X".to_owned()); + second + }, + }, + &[ + Token::Map { len: None }, + Token::Str("x"), + Token::Str("X"), + Token::Str("y"), + Token::Str("Y"), + Token::MapEnd, + ], + ); +} + #[test] fn test_flatten_unsupported_type() { #[derive(Debug, PartialEq, Serialize, Deserialize)]