From aff53e8dd4cb0e0c3fded780233fb8083d3c2895 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Mon, 18 Aug 2014 07:37:44 -0700 Subject: [PATCH] allow the deserializer to optionally handle missing fields This allows json to deserialize missing values as a `null`. --- serde_macros/src/lib.rs | 48 +++++++++-------------------------------- src/bench_enum.rs | 12 ++++++----- src/bench_map.rs | 12 ++++++----- src/bench_struct.rs | 12 ++++++----- src/bench_vec.rs | 24 ++++++++++++--------- src/de.rs | 11 +++++++--- src/json/mod.rs | 31 ++++++++++++++++++++++---- 7 files changed, 80 insertions(+), 70 deletions(-) diff --git a/serde_macros/src/lib.rs b/serde_macros/src/lib.rs index eac39999..fa141540 100644 --- a/serde_macros/src/lib.rs +++ b/serde_macros/src/lib.rs @@ -394,23 +394,18 @@ fn deserialize_struct_from_map( }) .collect(); - let fields_tuple = cx.expr_tuple( - span, - fields.iter() - .map(|&(name, span)| { - cx.expr_ident(span, name) - }) - .collect() - ); - - let fields_pats: Vec> = fields.iter() + let extract_fields: Vec> = fields.iter() .map(|&(name, span)| { - quote_pat!(cx, Some($name)) + let name_str = cx.expr_str(span, token::get_ident(name)); + quote_stmt!(cx, + let $name = match $name { + Some($name) => $name, + None => try!($deserializer.missing_field($name_str)), + }; + ) }) .collect(); - let fields_pat = cx.pat_tuple(span, fields_pats); - let result = cx.expr_struct_ident( span, type_ident, @@ -421,27 +416,6 @@ fn deserialize_struct_from_map( .collect() ); - let error_arms: Vec = fields.iter() - .map(|&(name, span)| { - let pats = fields.iter() - .map(|&(n, _)| { - if n == name { - quote_pat!(cx, None) - } else { - quote_pat!(cx, _) - } - }) - .collect(); - - let pat = cx.pat_tuple(span, pats); - let s = cx.expr_str(span, token::get_ident(name)); - - quote_arm!(cx, - $pat => Err($deserializer.missing_field_error($s)), - ) - }) - .collect(); - quote_expr!(cx, { $let_fields @@ -473,10 +447,8 @@ fn deserialize_struct_from_map( try!($deserializer.ignore_field(token)) } - match $fields_tuple { - $fields_pat => Ok($result), - $error_arms - } + $extract_fields + Ok($result) }) } diff --git a/src/bench_enum.rs b/src/bench_enum.rs index 815bb30d..1880ab14 100644 --- a/src/bench_enum.rs +++ b/src/bench_enum.rs @@ -240,11 +240,6 @@ mod deserializer { SyntaxError } - #[inline] - fn missing_field_error(&mut self, _field: &'static str) -> Error { - SyntaxError - } - #[inline] fn unexpected_name_error(&mut self, _token: de::Token) -> Error { SyntaxError @@ -254,6 +249,13 @@ mod deserializer { fn conversion_error(&mut self, _token: de::Token) -> Error { SyntaxError } + + #[inline] + fn missing_field< + T: de::Deserializable + >(&mut self, _field: &'static str) -> Result { + Err(SyntaxError) + } } } diff --git a/src/bench_map.rs b/src/bench_map.rs index 0faabcde..0ef94bbc 100644 --- a/src/bench_map.rs +++ b/src/bench_map.rs @@ -236,11 +236,6 @@ mod deserializer { SyntaxError } - #[inline] - fn missing_field_error(&mut self, _field: &'static str) -> Error { - SyntaxError - } - #[inline] fn unexpected_name_error(&mut self, _token: de::Token) -> Error { SyntaxError @@ -250,6 +245,13 @@ mod deserializer { fn conversion_error(&mut self, _token: de::Token) -> Error { SyntaxError } + + #[inline] + fn missing_field< + T: de::Deserializable + >(&mut self, _field: &'static str) -> Result { + Err(SyntaxError) + } } } diff --git a/src/bench_struct.rs b/src/bench_struct.rs index dba8d7f7..0c5f3144 100644 --- a/src/bench_struct.rs +++ b/src/bench_struct.rs @@ -360,11 +360,6 @@ mod deserializer { SyntaxError } - #[inline] - fn missing_field_error(&mut self, _field: &'static str) -> Error { - SyntaxError - } - #[inline] fn unexpected_name_error(&mut self, _token: de::Token) -> Error { SyntaxError @@ -374,6 +369,13 @@ mod deserializer { fn conversion_error(&mut self, _token: de::Token) -> Error { SyntaxError } + + #[inline] + fn missing_field< + T: de::Deserializable + >(&mut self, _field: &'static str) -> Result { + Err(SyntaxError) + } } } diff --git a/src/bench_vec.rs b/src/bench_vec.rs index 3ba5201f..83477ef0 100644 --- a/src/bench_vec.rs +++ b/src/bench_vec.rs @@ -304,11 +304,6 @@ mod deserializer { SyntaxError } - #[inline] - fn missing_field_error(&mut self, _field: &'static str) -> Error { - SyntaxError - } - #[inline] fn unexpected_name_error(&mut self, _token: de::Token) -> Error { SyntaxError @@ -318,6 +313,13 @@ mod deserializer { fn conversion_error(&mut self, _token: de::Token) -> Error { SyntaxError } + + #[inline] + fn missing_field< + T: de::Deserializable + >(&mut self, _field: &'static str) -> Result { + Err(SyntaxError) + } } pub struct U8Deserializer { @@ -374,11 +376,6 @@ mod deserializer { SyntaxError } - #[inline] - fn missing_field_error(&mut self, _field: &'static str) -> Error { - SyntaxError - } - #[inline] fn unexpected_name_error(&mut self, _token: de::Token) -> Error { SyntaxError @@ -388,6 +385,13 @@ mod deserializer { fn conversion_error(&mut self, _token: de::Token) -> Error { SyntaxError } + + #[inline] + fn missing_field< + T: de::Deserializable + >(&mut self, _field: &'static str) -> Result { + Err(SyntaxError) + } } } diff --git a/src/de.rs b/src/de.rs index cb9cda82..fd6ffa0c 100644 --- a/src/de.rs +++ b/src/de.rs @@ -191,7 +191,9 @@ pub trait Deserializer: Iterator> { /// Called when a `Deserializable` structure did not deserialize a field /// named `field`. - fn missing_field_error(&mut self, field: &'static str) -> E; + fn missing_field< + T: Deserializable + >(&mut self, field: &'static str) -> Result; /// Called when a deserializable has decided to not consume this token. fn ignore_field(&mut self, _token: Token) -> Result<(), E> { @@ -1161,8 +1163,11 @@ mod tests { SyntaxError } - fn missing_field_error(&mut self, _field: &'static str) -> Error { - IncompleteValue + #[inline] + fn missing_field< + T: Deserializable + >(&mut self, _field: &'static str) -> Result { + Err(SyntaxError) } } diff --git a/src/json/mod.rs b/src/json/mod.rs index 4db5fd15..4a25bd65 100644 --- a/src/json/mod.rs +++ b/src/json/mod.rs @@ -663,8 +663,13 @@ impl de::Deserializer for JsonDeserializer { SyntaxError(InvalidSyntax, 0, 0) } - fn missing_field_error(&mut self, field: &'static str) -> ParserError { - SyntaxError(MissingField(field), 0, 0) + #[inline] + fn missing_field< + T: de::Deserializable + >(&mut self, _field: &'static str) -> Result { + // JSON can represent `null` values as a missing value, so this isn't + // necessarily an error. + de::Deserializable::deserialize_token(self, de::Null) } // Special case treating options as a nullable value. @@ -2039,8 +2044,13 @@ impl> de::Deserializer for Parser { SyntaxError(InvalidSyntax, self.line, self.col) } - fn missing_field_error(&mut self, field: &'static str) -> ParserError { - SyntaxError(MissingField(field), self.line, self.col) + #[inline] + fn missing_field< + T: de::Deserializable + >(&mut self, _field: &'static str) -> Result { + // JSON can represent `null` values as a missing value, so this isn't + // necessarily an error. + de::Deserializable::deserialize_token(self, de::Null) } // Special case treating options as a nullable value. @@ -3066,6 +3076,19 @@ mod tests { ("null", None), ("\"jodhpurs\"", Some("jodhpurs".to_string())), ]); + + #[deriving(PartialEq, Show)] + #[deriving_serializable] + #[deriving_deserializable] + struct Foo { + x: Option, + } + + let value: Foo = from_str("{}").unwrap(); + assert_eq!(value, Foo { x: None }); + + let value: Foo = from_str("{ \"x\": 5 }").unwrap(); + assert_eq!(value, Foo { x: Some(5) }); } #[test]