Implement deserialize_with for variants

Complements variant serialize_with and closes #1013.
This commit is contained in:
Michael Smith
2017-08-14 14:39:29 -07:00
parent 5b815b7001
commit 9fc180e62f
8 changed files with 308 additions and 27 deletions
+88 -16
View File
@@ -11,6 +11,7 @@ extern crate serde_derive;
extern crate serde;
use self::serde::{Serialize, Serializer, Deserialize, Deserializer};
use self::serde::de::{self, Unexpected};
extern crate serde_test;
use self::serde_test::{Token, assert_tokens, assert_ser_tokens, assert_de_tokens,
@@ -811,18 +812,22 @@ fn test_serialize_with_enum() {
);
}
#[derive(Debug, PartialEq, Serialize)]
enum SerializeWithVariant {
#[derive(Debug, PartialEq, Serialize, Deserialize)]
enum WithVariant {
#[serde(serialize_with="serialize_unit_variant_as_i8")]
#[serde(deserialize_with="deserialize_i8_as_unit_variant")]
Unit,
#[serde(serialize_with="SerializeWith::serialize_with")]
#[serde(deserialize_with="DeserializeWith::deserialize_with")]
Newtype(i32),
#[serde(serialize_with="serialize_some_other_variant")]
#[serde(serialize_with="serialize_variant_as_string")]
#[serde(deserialize_with="deserialize_string_as_variant")]
Tuple(String, u8),
#[serde(serialize_with="serialize_some_other_variant")]
#[serde(serialize_with="serialize_variant_as_string")]
#[serde(deserialize_with="deserialize_string_as_variant")]
Struct {
f1: String,
f2: u8,
@@ -835,45 +840,112 @@ fn serialize_unit_variant_as_i8<S>(serializer: S) -> Result<S::Ok, S::Error>
serializer.serialize_i8(0)
}
fn serialize_some_other_variant<S>(f1: &String,
f2: &u8,
serializer: S)
-> Result<S::Ok, S::Error>
fn deserialize_i8_as_unit_variant<'de, D>(deserializer: D) -> Result<(), D::Error>
where D: Deserializer<'de>,
{
let n = i8::deserialize(deserializer)?;
match n {
0 => Ok(()),
_ => Err(de::Error::invalid_value(Unexpected::Signed(n as i64), &"0")),
}
}
fn serialize_variant_as_string<S>(f1: &String,
f2: &u8,
serializer: S)
-> Result<S::Ok, S::Error>
where S: Serializer,
{
serializer.serialize_str(format!("{};{:?}", f1, f2).as_str())
}
fn deserialize_string_as_variant<'de, D>(deserializer: D) -> Result<(String, u8), D::Error>
where D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
let mut pieces = s.split(';');
let f1 = match pieces.next() {
Some(x) => x,
None => return Err(de::Error::invalid_length(0, &"2")),
};
let f2 = match pieces.next() {
Some(x) => x,
None => return Err(de::Error::invalid_length(1, &"2")),
};
let f2 = match f2.parse() {
Ok(n) => n,
Err(_) => {
return Err(de::Error::invalid_value(Unexpected::Str(f2), &"an 8-bit signed integer"));
}
};
Ok((f1.into(), f2))
}
#[test]
fn test_serialize_with_variant() {
assert_ser_tokens(
&SerializeWithVariant::Unit,
&WithVariant::Unit,
&[
Token::NewtypeVariant { name: "SerializeWithVariant", variant: "Unit" },
Token::NewtypeVariant { name: "WithVariant", variant: "Unit" },
Token::I8(0),
],
);
assert_ser_tokens(
&SerializeWithVariant::Newtype(123),
&WithVariant::Newtype(123),
&[
Token::NewtypeVariant { name: "SerializeWithVariant", variant: "Newtype" },
Token::NewtypeVariant { name: "WithVariant", variant: "Newtype" },
Token::Bool(true),
],
);
assert_ser_tokens(
&SerializeWithVariant::Tuple("hello".into(), 0),
&WithVariant::Tuple("hello".into(), 0),
&[
Token::NewtypeVariant { name: "SerializeWithVariant", variant: "Tuple" },
Token::NewtypeVariant { name: "WithVariant", variant: "Tuple" },
Token::Str("hello;0"),
],
);
assert_ser_tokens(
&SerializeWithVariant::Struct { f1: "world".into(), f2: 1 },
&WithVariant::Struct { f1: "world".into(), f2: 1 },
&[
Token::NewtypeVariant { name: "SerializeWithVariant", variant: "Struct" },
Token::NewtypeVariant { name: "WithVariant", variant: "Struct" },
Token::Str("world;1"),
],
);
}
#[test]
fn test_deserialize_with_variant() {
assert_de_tokens(
&WithVariant::Unit,
&[
Token::NewtypeVariant { name: "WithVariant", variant: "Unit" },
Token::I8(0),
],
);
assert_de_tokens(
&WithVariant::Newtype(123),
&[
Token::NewtypeVariant { name: "WithVariant", variant: "Newtype" },
Token::Bool(true),
],
);
assert_de_tokens(
&WithVariant::Tuple("hello".into(), 0),
&[
Token::NewtypeVariant { name: "WithVariant", variant: "Tuple" },
Token::Str("hello;0"),
],
);
assert_de_tokens(
&WithVariant::Struct { f1: "world".into(), f2: 1 },
&[
Token::NewtypeVariant { name: "WithVariant", variant: "Struct" },
Token::Str("world;1"),
],
);