diff --git a/src/libserialize/json.rs b/src/libserialize/json.rs index e28c51d517ea3..b4838196f4e80 100644 --- a/src/libserialize/json.rs +++ b/src/libserialize/json.rs @@ -104,6 +104,41 @@ fn main() { } ``` +If you need to work with a JSON object which has keys that are not Rust +identifiers, you can tag each such field with the `encoded_name` attribute +to specify the name to use when serializing/deserializing. + +```rust +extern crate serialize; +use serialize::json; + +#[deriving(Encodable, Decodable)] +struct Data { + metric: String, + #[encoded_name = "metric-value"] + value: int +} + +fn main() { + let input = r#"{"metric":"foo","metric-value":44}"#; + + // Decode the input JSON + let data: Data = json::decode(input).unwrap(); + + // Modify the input + let data = Data { + value: 32, + ..data + }; + + // Encode it as JSON again + let out: String = json::encode(&data); + + println!("data: {}", out); + // data: {"metric":"foo","metric-value":32} +} +``` + ## Using the `ToJson` trait The examples above use the `ToJson` trait to generate the JSON string, which required @@ -3319,6 +3354,31 @@ mod tests { assert_eq!(None::.to_json(), Null); } + #[deriving(Encodable, Decodable, PartialEq, Show)] + struct Foo { + key: String, + #[encoded_name = "another-key"] + another_key: String, + metric: int, + #[encoded_name = "metric-2"] + another_metric: int + } + + #[test] + fn test_encoded_name_attribute() { + use super::{encode, decode}; + + let s = r#"{"key":"foo","another-key":"bar","metric":12,"metric-2":21}"#; + let f = Foo { + key: "foo".to_string(), + another_key: "bar".to_string(), + metric: 12, + another_metric: 21 + }; + assert_eq!(encode(&f), s.to_string()); + assert_eq!(decode::(s).unwrap(), f); + } + #[bench] fn bench_streaming_small(b: &mut Bencher) { b.iter( || { diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index e8b9ec9628f7d..01d8f31d1850d 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -231,7 +231,10 @@ pub fn first_attr_value_str_by_name(attrs: &[Attribute], name: &str) -> Option { attrs.iter() .find(|at| at.check_name(name)) - .and_then(|at| at.value_str()) + .and_then(|at| { + mark_used(at); + at.value_str() + }) } pub fn last_meta_item_value_str_by_name(items: &[Gc], name: &str) diff --git a/src/libsyntax/ext/deriving/decodable.rs b/src/libsyntax/ext/deriving/decodable.rs index d909ffd2b49fb..7797ced1ca4f5 100644 --- a/src/libsyntax/ext/deriving/decodable.rs +++ b/src/libsyntax/ext/deriving/decodable.rs @@ -14,6 +14,7 @@ encodable.rs for more. */ use ast::{MetaItem, Item, Expr, MutMutable, Ident}; +use attr; use codemap::Span; use ext::base::ExtCtxt; use ext::build::AstBuilder; @@ -76,7 +77,7 @@ fn decodable_substructure(cx: &mut ExtCtxt, trait_span: Span, let lambdadecode = cx.lambda_expr_1(trait_span, calldecode, blkarg); return match *substr.fields { - StaticStruct(_, ref summary) => { + StaticStruct(struct_def, ref summary) => { let nfields = match *summary { Unnamed(ref fields) => fields.len(), Named(ref fields) => fields.len() @@ -88,6 +89,9 @@ fn decodable_substructure(cx: &mut ExtCtxt, trait_span: Span, substr.type_ident, summary, |cx, span, name, field| { + let attrs = struct_def.fields[field].node.attrs.as_slice(); + let name = attr::first_attr_value_str_by_name(attrs, "encoded_name") + .unwrap_or(name); cx.expr_try(span, cx.expr_method_call(span, blkdecoder, read_struct_field, vec!(cx.expr_str(span, name), diff --git a/src/libsyntax/ext/deriving/encodable.rs b/src/libsyntax/ext/deriving/encodable.rs index 7e289e7676aa9..2bc905e770030 100644 --- a/src/libsyntax/ext/deriving/encodable.rs +++ b/src/libsyntax/ext/deriving/encodable.rs @@ -80,6 +80,7 @@ //! ``` use ast::{MetaItem, Item, Expr, ExprRet, MutMutable, LitNil}; +use attr; use codemap::Span; use ext::base::ExtCtxt; use ext::build::AstBuilder; @@ -144,13 +145,16 @@ fn encodable_substructure(cx: &mut ExtCtxt, trait_span: Span, let mut stmts = Vec::new(); let last = fields.len() - 1; for (i, &FieldInfo { + ref attrs, name, self_, span, .. }) in fields.iter().enumerate() { let name = match name { - Some(id) => token::get_ident(id), + Some(id) => + attr::first_attr_value_str_by_name(attrs.as_slice(), "encoded_name") + .unwrap_or(token::get_ident(id)), None => { token::intern_and_get_ident(format!("_field{}", i).as_slice()) diff --git a/src/libsyntax/ext/deriving/generic/mod.rs b/src/libsyntax/ext/deriving/generic/mod.rs index 871f277a2da28..b37d141e00a66 100644 --- a/src/libsyntax/ext/deriving/generic/mod.rs +++ b/src/libsyntax/ext/deriving/generic/mod.rs @@ -103,7 +103,8 @@ //! //! ~~~text //! Struct(~[FieldInfo { -//! span: +//! attrs: , +//! span: , //! name: Some(), //! self_: , //! other: ~[, //! span: , //! name: None, //! @@ -129,7 +131,8 @@ //! ~~~text //! EnumMatching(0, , //! ~[FieldInfo { -//! span: +//! attrs: , +//! span: , //! name: None, //! self_: , //! other: ~[] @@ -141,7 +144,8 @@ //! ~~~text //! EnumMatching(1, , //! ~[FieldInfo { -//! span: +//! attrs: , +//! span: , //! name: Some(), //! self_: , //! other: ~[] @@ -259,6 +263,10 @@ pub struct Substructure<'a> { /// Summary of the relevant parts of a struct/enum field. pub struct FieldInfo { + /// The attributes on the field in the definition + /// for normal structs/struct enum variants + pub attrs: Vec, + /// The field's span pub span: Span, /// None for tuple structs/normal enum variants, Some for normal /// structs/struct enum variants. @@ -724,13 +732,14 @@ impl<'a> MethodDef<'a> { raw_fields.get(0) .iter() .enumerate() - .map(|(i, &(span, opt_id, field))| { + .map(|(i, &(ref attrs, span, opt_id, field))| { let other_fields = raw_fields.tail().iter().map(|l| { match l.get(i) { - &(_, _, ex) => ex + &(_, _, _, ex) => ex } }).collect(); FieldInfo { + attrs: attrs.clone(), span: span, name: opt_id, self_: field, @@ -900,7 +909,7 @@ impl<'a> MethodDef<'a> { // These self_pats have form Variant1, Variant2, ... let self_pats : Vec<(Gc, - Vec<(Span, Option, Gc)>)>; + Vec<(Vec, Span, Option, Gc)>)>; self_pats = self_arg_names.iter() .map(|self_arg_name| trait_.create_enum_variant_pattern( @@ -934,7 +943,7 @@ impl<'a> MethodDef<'a> { field_tuples = self_arg_fields.iter().enumerate() // For each arg field of self, pull out its getter expr ... - .map(|(field_index, &(sp, opt_ident, self_getter_expr))| { + .map(|(field_index, &(ref attrs, sp, opt_ident, self_getter_expr))| { // ... but FieldInfo also wants getter expr // for matching other arguments of Self type; // so walk across the *other* self_pats and @@ -944,7 +953,7 @@ impl<'a> MethodDef<'a> { let others = self_pats.tail().iter() .map(|&(_pat, ref fields)| { - let &(_, _opt_ident, other_getter_expr) = + let &(_, _, _opt_ident, other_getter_expr) = fields.get(field_index); // All Self args have same variant, so @@ -956,10 +965,12 @@ impl<'a> MethodDef<'a> { other_getter_expr }).collect::>>(); - FieldInfo { span: sp, - name: opt_ident, - self_: self_getter_expr, - other: others, + FieldInfo { + attrs: attrs.clone(), + span: sp, + name: opt_ident, + self_: self_getter_expr, + other: others, } }).collect::>(); @@ -1204,7 +1215,8 @@ impl<'a> TraitDef<'a> { struct_def: &StructDef, prefix: &str, mutbl: ast::Mutability) - -> (Gc, Vec<(Span, Option, Gc)>) { + -> (Gc, Vec<(Vec, Span, Option, Gc)>) { + if struct_def.fields.is_empty() { return ( cx.pat_ident_binding_mode( @@ -1239,7 +1251,7 @@ impl<'a> TraitDef<'a> { paths.push(codemap::Spanned{span: sp, node: ident}); let val = cx.expr( sp, ast::ExprParen(cx.expr_deref(sp, cx.expr_path(cx.path_ident(sp,ident))))); - ident_expr.push((sp, opt_id, val)); + ident_expr.push((struct_field.node.attrs.clone(), sp, opt_id, val)); } let subpats = self.create_subpatterns(cx, paths, mutbl); @@ -1247,7 +1259,7 @@ impl<'a> TraitDef<'a> { // struct_type is definitely not Unknown, since struct_def.fields // must be nonempty to reach here let pattern = if struct_type == Record { - let field_pats = subpats.iter().zip(ident_expr.iter()).map(|(&pat, &(_, id, _))| { + let field_pats = subpats.iter().zip(ident_expr.iter()).map(|(&pat, &(_, _, id, _))| { // id is guaranteed to be Some ast::FieldPat { ident: id.unwrap(), pat: pat } }).collect(); @@ -1264,7 +1276,7 @@ impl<'a> TraitDef<'a> { variant: &ast::Variant, prefix: &str, mutbl: ast::Mutability) - -> (Gc, Vec<(Span, Option, Gc)> ) { + -> (Gc, Vec<(Vec, Span, Option, Gc)> ) { let variant_ident = variant.node.name; match variant.node.kind { ast::TupleVariantKind(ref variant_args) => { @@ -1285,7 +1297,7 @@ impl<'a> TraitDef<'a> { paths.push(path1); let expr_path = cx.expr_path(cx.path_ident(sp, ident)); let val = cx.expr(sp, ast::ExprParen(cx.expr_deref(sp, expr_path))); - ident_expr.push((sp, None, val)); + ident_expr.push((Vec::new(), sp, None, val)); } let subpats = self.create_subpatterns(cx, paths, mutbl);