Skip to content
6 changes: 4 additions & 2 deletions src/de/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,10 +283,12 @@ impl<'de, 'a> de::Deserializer<'de> for MapKey<'a, 'de> {
self.deserialize_str(visitor)
}

fn deserialize_ignored_any<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
unreachable!()
// Even if we’re ignoring the contents of the map, we still need to
// deserialize the string here in order to chomp the key’s characters.
self.deserialize_str(visitor)
}
}
102 changes: 98 additions & 4 deletions src/de/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ macro_rules! deserialize_signed {
impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> {
type Error = Error;

/// Unsupported. Can’t parse a value without knowing its expected type.
fn deserialize_any<V>(self, _visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
Expand Down Expand Up @@ -470,13 +471,15 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> {
}
}

/// Unsupported
fn deserialize_bytes<V>(self, _visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
unreachable!()
}

/// Unsupported
fn deserialize_byte_buf<V>(self, _visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
Expand All @@ -498,20 +501,23 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> {
}
}

/// Unsupported. Use a more specific deserialize_* method
fn deserialize_unit<V>(self, _visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
unreachable!()
}

/// Unsupported. Use a more specific deserialize_* method
fn deserialize_unit_struct<V>(self, _name: &'static str, _visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
unreachable!()
}

/// Unsupported. We can’t parse newtypes because we don’t know the underlying type.
fn deserialize_newtype_struct<V>(self, _name: &'static str, _visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
Expand Down Expand Up @@ -547,14 +553,17 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> {
self,
_name: &'static str,
_len: usize,
_visitor: V,
visitor: V,
) -> Result<V::Value>
where
V: Visitor<'de>,
{
unreachable!()
self.deserialize_seq(visitor)
}

/// Unsupported. Can’t make an arbitrary-sized map in no-std. Use a struct with a
/// known format, or implement a custom map deserializer / visitor:
/// https://serde.rs/deserialize-map.html
fn deserialize_map<V>(self, _visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
Expand Down Expand Up @@ -608,11 +617,30 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> {
self.deserialize_str(visitor)
}

fn deserialize_ignored_any<V>(self, _visitor: V) -> Result<V::Value>
/// Used to throw out fields from JSON objects that we don’t want to
/// keep in our structs.
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
unreachable!()
match self.parse_whitespace().ok_or(Error::EofWhileParsingValue)? {
b'"' => self.deserialize_str(visitor),
b'[' => self.deserialize_seq(visitor),
b'{' => self.deserialize_struct("ignored", &[], visitor),
b',' | b'}' | b']' => Err(Error::ExpectedSomeValue),
// If it’s something else then we chomp until we get to an end delimiter.
// This does technically allow for illegal JSON since we’re just ignoring
// characters rather than parsing them.
_ => loop {
match self.peek() {
// The visitor is expected to be UnknownAny’s visitor, which
// implements visit_unit to return its unit Ok result.
Some(b',') | Some(b'}') | Some(b']') => break visitor.visit_unit(),
Some(_) => self.eat_char(),
None => break Err(Error::EofWhileParsingString),
}
},
}
}
}

Expand Down Expand Up @@ -772,6 +800,72 @@ mod tests {
assert!(crate::from_str::<Temperature>(r#"{ "temperature": -1 }"#).is_err());
}

#[test]
fn struct_tuple() {
#[derive(Debug, Deserialize, PartialEq)]
struct Xy(i8, i8);

assert_eq!(crate::from_str(r#"[10, 20]"#), Ok(Xy(10, 20)));
assert_eq!(crate::from_str(r#"[10, -20]"#), Ok(Xy(10, -20)));

// wrong number of args
match crate::from_str::<Xy>(r#"[10]"#) {
Err(super::Error::Custom(_)) => {},
_ => panic!("expect custom error"),
}
assert_eq!(crate::from_str::<Xy>(r#"[10, 20, 30]"#), Err(crate::de::Error::TrailingCharacters));
}

#[test]
fn ignoring_extra_fields() {
#[derive(Debug, Deserialize, PartialEq)]
struct Temperature {
temperature: u8,
}

assert_eq!(
crate::from_str(r#"{ "temperature": 20, "high": 80, "low": -10, "updated": true }"#),
Ok(Temperature { temperature: 20 })
);

assert_eq!(
crate::from_str(
r#"{ "temperature": 20, "conditions": "windy", "forecast": "cloudy" }"#
),
Ok(Temperature { temperature: 20 })
);

assert_eq!(
crate::from_str(r#"{ "temperature": 20, "hourly_conditions": ["windy", "rainy"] }"#),
Ok(Temperature { temperature: 20 })
);

assert_eq!(
crate::from_str(r#"{ "temperature": 20, "source": { "station": "dock", "sensors": ["front", "back"] } }"#),
Ok(Temperature { temperature: 20 })
);

assert_eq!(
crate::from_str(r#"{ "temperature": 20, "invalid": this-is-ignored }"#),
Ok(Temperature { temperature: 20 })
);

assert_eq!(
crate::from_str::<Temperature>(r#"{ "temperature": 20, "broken": }"#),
Err(crate::de::Error::ExpectedSomeValue)
);

assert_eq!(
crate::from_str::<Temperature>(r#"{ "temperature": 20, "broken": [ }"#),
Err(crate::de::Error::ExpectedSomeValue)
);

assert_eq!(
crate::from_str::<Temperature>(r#"{ "temperature": 20, "broken": ] }"#),
Err(crate::de::Error::ExpectedSomeValue)
);
}

// See https://iot.mozilla.org/wot/#thing-resource
#[test]
fn wot() {
Expand Down