diff --git a/serde_codegen/src/attr.rs b/serde_codegen/src/attr.rs index 9f08969d..e4015546 100644 --- a/serde_codegen/src/attr.rs +++ b/serde_codegen/src/attr.rs @@ -16,14 +16,20 @@ pub enum FieldNames { /// Represents field attribute information pub struct FieldAttrs { + skip_serializing_field: bool, names: FieldNames, use_default: bool, } impl FieldAttrs { /// Create a FieldAttr with a single default field name - pub fn new(default_value: bool, name: P) -> FieldAttrs { + pub fn new( + skip_serializing_field: bool, + default_value: bool, + name: P, + ) -> FieldAttrs { FieldAttrs { + skip_serializing_field: skip_serializing_field, names: FieldNames::Global(name), use_default: default_value, } @@ -31,12 +37,14 @@ impl FieldAttrs { /// Create a FieldAttr with format specific field names pub fn new_with_formats( + skip_serializing_field: bool, default_value: bool, default_name: P, formats: HashMap, P>, ) -> FieldAttrs { FieldAttrs { - names: FieldNames::Format { + skip_serializing_field: skip_serializing_field, + names: FieldNames::Format { formats: formats, default: default_name, }, @@ -104,4 +112,9 @@ impl FieldAttrs { pub fn use_default(&self) -> bool { self.use_default } + + /// Predicate for ignoring a field when serializing a value + pub fn skip_serializing_field(&self) -> bool { + self.skip_serializing_field + } } diff --git a/serde_codegen/src/de.rs b/serde_codegen/src/de.rs index 3f949df0..f88f7298 100644 --- a/serde_codegen/src/de.rs +++ b/serde_codegen/src/de.rs @@ -483,6 +483,7 @@ fn deserialize_item_enum( enum_def.variants.iter() .map(|variant| attr::FieldAttrs::new( + false, true, builder.expr().str(variant.node.name))) .collect() diff --git a/serde_codegen/src/field.rs b/serde_codegen/src/field.rs index 676e6cb7..6f10eacf 100644 --- a/serde_codegen/src/field.rs +++ b/serde_codegen/src/field.rs @@ -58,10 +58,18 @@ fn default_value(mi: &ast::MetaItem) -> bool { } } +fn skip_serializing_field(mi: &ast::MetaItem) -> bool { + if let ast::MetaItem_::MetaWord(ref n) = mi.node { + n == &"skip_serializing" + } else { + false + } +} + fn field_attrs<'a>( builder: &aster::AstBuilder, field: &'a ast::StructField, -) -> (Rename<'a>, bool) { +) -> (Rename<'a>, bool, bool) { field.node.attrs.iter() .find(|sa| { if let ast::MetaList(ref n, _) = sa.node.value.node { @@ -73,15 +81,18 @@ fn field_attrs<'a>( .and_then(|sa| { if let ast::MetaList(_, ref vals) = sa.node.value.node { attr::mark_used(&sa); - Some((vals.iter() - .fold(None, |v, mi| v.or(rename(builder, mi))) - .unwrap_or(Rename::None), - vals.iter().any(|mi| default_value(mi)))) + Some(( + vals.iter() + .fold(None, |v, mi| v.or(rename(builder, mi))) + .unwrap_or(Rename::None), + vals.iter().any(|mi| default_value(mi)), + vals.iter().any(|mi| skip_serializing_field(mi)), + )) } else { - Some((Rename::None, false)) + Some((Rename::None, false, false)) } }) - .unwrap_or((Rename::None, false)) + .unwrap_or((Rename::None, false, false)) } pub fn struct_field_attrs( @@ -92,23 +103,26 @@ pub fn struct_field_attrs( struct_def.fields.iter() .map(|field| { match field_attrs(builder, field) { - (Rename::Global(rename), default_value) => + (Rename::Global(rename), default_value, skip_serializing_field) => FieldAttrs::new( + skip_serializing_field, default_value, builder.expr().build_lit(P(rename.clone()))), - (Rename::Format(renames), default_value) => { + (Rename::Format(renames), default_value, skip_serializing_field) => { let mut res = HashMap::new(); res.extend( renames.into_iter() .map(|(k,v)| (k, builder.expr().build_lit(P(v.clone()))))); FieldAttrs::new_with_formats( + skip_serializing_field, default_value, default_field_name(cx, builder, field.node.kind), res) }, - (Rename::None, default_value) => { + (Rename::None, default_value, skip_serializing_field) => { FieldAttrs::new( + skip_serializing_field, default_value, default_field_name(cx, builder, field.node.kind)) } diff --git a/serde_codegen/src/ser.rs b/serde_codegen/src/ser.rs index 8c74e119..ab4f21ea 100644 --- a/serde_codegen/src/ser.rs +++ b/serde_codegen/src/ser.rs @@ -550,6 +550,7 @@ fn serialize_struct_visitor( let arms: Vec = field_attrs.into_iter() .zip(value_exprs) + .filter(|&(ref field, _)| !field.skip_serializing_field()) .enumerate() .map(|(i, (field, value_expr))| { let key_expr = field.serializer_key_expr(cx); diff --git a/serde_tests/tests/test_annotations.rs b/serde_tests/tests/test_annotations.rs index 9a21e0bf..9c963f8e 100644 --- a/serde_tests/tests/test_annotations.rs +++ b/serde_tests/tests/test_annotations.rs @@ -1,3 +1,4 @@ +use std::default; use serde_json; #[derive(Debug, PartialEq, Serialize, Deserialize)] @@ -30,6 +31,12 @@ enum SerEnum { }, } +#[derive(Debug, PartialEq, Deserialize, Serialize)] +struct SkipSerializingFields { + a: i8, + #[serde(skip_serializing, default)] + b: A, +} #[test] fn test_default() { @@ -71,3 +78,13 @@ fn test_enum_format_rename() { let deserialized_value = serde_json::from_str(ans).unwrap(); assert_eq!(value, deserialized_value); } + +#[test] +fn test_skip_serializing_fields() { + let value = SkipSerializingFields { a: 1, b: 2 }; + let serialized_value = serde_json::to_string(&value).unwrap(); + assert_eq!(serialized_value, "{\"a\":1}"); + + let deserialized_value: SkipSerializingFields<_> = serde_json::from_str(&serialized_value).unwrap(); + assert_eq!(SkipSerializingFields { a: 1, b: 0 }, deserialized_value); +}