Add support for serializing newtype structs

This enables formats like JSON to serialize newtype wrapper
structs without the normal object wrapper. Consider types like:

```rust
struct Point {
    x: u32,
    y: u32,
}

stuct MyPoint(Point);
```

Before this patch, `MyPoint(1,2)` would get serialized as
`[{"x":1,"y":2}]`, but now it gets serialized as `{"x":1,"y"2}`.
This commit is contained in:
Erick Tryzelaar 2015-07-30 09:38:09 -07:00
parent 6715fb6652
commit 97528b59cf
7 changed files with 180 additions and 41 deletions

View File

@ -208,8 +208,8 @@ pub trait Deserializer {
self.visit(visitor)
}
/// This method hints that the `Deserialize` type is expecting a named unit. This allows
/// deserializers to a named unit that aren't tagged as a named unit.
/// This method hints that the `Deserialize` type is expecting a unit struct. This allows
/// deserializers to a unit struct that aren't tagged as a unit struct.
#[inline]
fn visit_unit_struct<V>(&mut self,
_name: &'static str,
@ -219,6 +219,17 @@ pub trait Deserializer {
self.visit_unit(visitor)
}
/// This method hints that the `Deserialize` type is expecting a newtype struct. This allows
/// deserializers to a newtype struct that aren't tagged as a newtype struct.
#[inline]
fn visit_newtype_struct<V>(&mut self,
name: &'static str,
visitor: V) -> Result<V::Value, Self::Error>
where V: Visitor,
{
self.visit_tuple_struct(name, 1, visitor)
}
/// This method hints that the `Deserialize` type is expecting a tuple struct. This allows
/// deserializers to parse sequences that aren't tagged as sequences.
#[inline]
@ -415,6 +426,12 @@ pub trait Visitor {
Err(Error::syntax_error())
}
fn visit_newtype_struct<D>(&mut self, _deserializer: &mut D) -> Result<Self::Value, D::Error>
where D: Deserializer,
{
Err(Error::syntax_error())
}
fn visit_seq<V>(&mut self, _visitor: V) -> Result<Self::Value, V::Error>
where V: SeqVisitor,
{

View File

@ -125,6 +125,18 @@ pub trait Serializer {
self.visit_unit()
}
/// The `visit_newtype_struct` allows a tuple struct with a single element, also known as a
/// newtyped value, to be more efficiently serialized than a tuple struct with multiple items.
/// By default it just serializes the value as a tuple struct sequence.
#[inline]
fn visit_newtype_struct<T>(&mut self,
name: &'static str,
value: T) -> Result<(), Self::Error>
where T: Serialize,
{
self.visit_tuple_struct(name, Some(value))
}
/// The `visit_newtype_variant` allows a variant with a single item to be more efficiently
/// serialized than a variant with multiple items. By default it just serializes the value as a
/// tuple variant sequence.

View File

@ -129,15 +129,24 @@ fn deserialize_item_struct(
}
}
match (named_fields.is_empty(), unnamed_fields == 0) {
(true, true) => {
match (named_fields.is_empty(), unnamed_fields) {
(true, 0) => {
deserialize_unit_struct(
cx,
&builder,
item.ident,
)
}
(true, false) => {
(true, 1) => {
deserialize_newtype_struct(
cx,
&builder,
item.ident,
impl_generics,
ty,
)
}
(true, _) => {
deserialize_tuple_struct(
cx,
&builder,
@ -147,7 +156,7 @@ fn deserialize_item_struct(
unnamed_fields,
)
}
(false, true) => {
(false, 0) => {
deserialize_struct(
cx,
&builder,
@ -157,7 +166,7 @@ fn deserialize_item_struct(
struct_def,
)
}
(false, false) => {
(false, _) => {
cx.bug("struct has named and unnamed fields")
}
}
@ -278,6 +287,58 @@ fn deserialize_unit_struct(
})
}
fn deserialize_newtype_struct(
cx: &ExtCtxt,
builder: &aster::AstBuilder,
type_ident: Ident,
impl_generics: &ast::Generics,
ty: P<ast::Ty>,
) -> P<ast::Expr> {
let where_clause = &impl_generics.where_clause;
let (visitor_item, visitor_ty, visitor_expr, visitor_generics) =
deserialize_visitor(
builder,
impl_generics,
vec![deserializer_ty_param(builder)],
vec![deserializer_ty_arg(builder)],
);
let visit_seq_expr = deserialize_seq(
cx,
builder,
builder.path().id(type_ident).build(),
1,
);
let type_name = builder.expr().str(type_ident);
quote_expr!(cx, {
$visitor_item
impl $visitor_generics ::serde::de::Visitor for $visitor_ty $where_clause {
type Value = $ty;
#[inline]
fn visit_newtype_struct<D>(&mut self, deserializer: &mut D) -> Result<Self::Value, D::Error>
where D: ::serde::de::Deserializer,
{
let value = try!(::serde::de::Deserialize::deserialize(deserializer));
Ok($type_ident(value))
}
#[inline]
fn visit_seq<__V>(&mut self, mut visitor: __V) -> ::std::result::Result<$ty, __V::Error>
where __V: ::serde::de::SeqVisitor,
{
$visit_seq_expr
}
}
deserializer.visit_newtype_struct($type_name, $visitor_expr)
})
}
fn deserialize_tuple_struct(
cx: &ExtCtxt,
builder: &aster::AstBuilder,

View File

@ -124,15 +124,22 @@ fn serialize_item_struct(
}
}
match (named_fields.is_empty(), unnamed_fields == 0) {
(true, true) => {
match (named_fields.is_empty(), unnamed_fields) {
(true, 0) => {
serialize_unit_struct(
cx,
&builder,
item.ident,
)
}
(true, false) => {
(true, 1) => {
serialize_newtype_struct(
cx,
&builder,
item.ident,
)
}
(true, _) => {
serialize_tuple_struct(
cx,
&builder,
@ -142,7 +149,7 @@ fn serialize_item_struct(
unnamed_fields,
)
}
(false, true) => {
(false, 0) => {
serialize_struct(
cx,
&builder,
@ -153,7 +160,7 @@ fn serialize_item_struct(
named_fields,
)
}
(false, false) => {
(false, _) => {
cx.bug("struct has named and unnamed fields")
}
}
@ -169,6 +176,16 @@ fn serialize_unit_struct(
quote_expr!(cx, serializer.visit_unit_struct($type_name))
}
fn serialize_newtype_struct(
cx: &ExtCtxt,
builder: &aster::AstBuilder,
type_ident: Ident
) -> P<ast::Expr> {
let type_name = builder.expr().str(type_ident);
quote_expr!(cx, serializer.visit_newtype_struct($type_name, &self.0))
}
fn serialize_tuple_struct(
cx: &ExtCtxt,
builder: &aster::AstBuilder,

View File

@ -453,6 +453,15 @@ impl<Iter> de::Deserializer for Deserializer<Iter>
}
}
#[inline]
fn visit_newtype_struct<V>(&mut self,
_name: &str,
mut visitor: V) -> Result<V::Value, Error>
where V: de::Visitor,
{
visitor.visit_newtype_struct(self)
}
#[inline]
fn visit_enum<V>(&mut self,
_name: &str,

View File

@ -158,6 +158,16 @@ impl<W, F> ser::Serializer for Serializer<W, F>
self.writer.write_all(b"null")
}
/// Override `visit_newtype_struct` to serialize newtypes without an object wrapper.
#[inline]
fn visit_newtype_struct<T>(&mut self,
_name: &'static str,
value: T) -> Result<(), Self::Error>
where T: ser::Serialize,
{
value.serialize(self)
}
#[inline]
fn visit_unit_variant(&mut self,
_name: &str,

View File

@ -137,18 +137,17 @@ pub struct GenericStruct<T> {
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct GenericTupleStruct<T>(T);
pub struct GenericNewtypeStruct<T>(T);
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum GenericEnumSeq<T, E> {
Ok(T),
Err(E),
}
pub struct GenericTupleStruct<T, U>(T, U);
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum GenericEnumMap<T, E> {
Ok { x: T },
Err { x: E },
pub enum GenericEnum<T, U> {
Unit,
Newtype(T),
Seq(T, U),
Map { x: T, y: U },
}
#[test]
@ -496,28 +495,42 @@ fn test_lifetimes() {
);
}
#[test]
fn test_generic() {
macro_rules! declare_tests {
($($ty:ty : $value:expr => $str:expr,)+) => {
$({
let value: $ty = $value;
macro_rules! declare_tests {
($($name:ident { $($ty:ty : $value:expr => $str:expr,)+ })+) => {
$(
#[test]
fn $name() {
$(
let value: $ty = $value;
let string = serde_json::to_string(&value).unwrap();
assert_eq!(string, $str);
let string = serde_json::to_string(&value).unwrap();
assert_eq!(string, $str);
let expected: $ty = serde_json::from_str(&string).unwrap();
assert_eq!(value, expected);
})+
}
let expected: $ty = serde_json::from_str(&string).unwrap();
assert_eq!(value, expected);
)+
}
)+
}
}
declare_tests! {
test_generic_struct {
GenericStruct<u32> : GenericStruct { x: 5 } => "{\"x\":5}",
}
test_generic_newtype_struct {
GenericNewtypeStruct<u32> : GenericNewtypeStruct(5) => "5",
}
test_generic_tuple_struct {
GenericTupleStruct<u32, u32> : GenericTupleStruct(5, 6) => "[5,6]",
}
test_generic_enum_newtype {
GenericEnum<u32, u32> : GenericEnum::Newtype(5) => "{\"Newtype\":5}",
}
test_generic_enum_seq {
GenericEnum<u32, u32> : GenericEnum::Seq(5, 6) => "{\"Seq\":[5,6]}",
}
test_generic_enum_map {
GenericEnum<u32, u32> : GenericEnum::Map { x: 5, y: 6 } => "{\"Map\":{\"x\":5,\"y\":6}}",
}
declare_tests!(
GenericStruct<u32> : GenericStruct { x: 5 } => "{\"x\":5}",
GenericTupleStruct<u32> : GenericTupleStruct(5) => "[5]",
GenericEnumSeq<u32, u32> : GenericEnumSeq::Ok(5) => "{\"Ok\":5}",
GenericEnumSeq<u32, u32> : GenericEnumSeq::Err(5) => "{\"Err\":5}",
GenericEnumMap<u32, u32> : GenericEnumMap::Ok { x: 5 } => "{\"Ok\":{\"x\":5}}",
GenericEnumMap<u32, u32> : GenericEnumMap::Err { x: 5 } => "{\"Err\":{\"x\":5}}",
);
}