From 7bb77481c7554925376456401e9b3209c36f065b Mon Sep 17 00:00:00 2001 From: Stuart Small Date: Tue, 24 May 2022 16:20:22 -0600 Subject: [PATCH] Parse nulls as None on nested serde structs When parsing a structure nested inside an untagged enum serde treats keys with a null value as Unit instead of calling visit_none. This causes an error of "invalid type: null, expected an RFC3339-formatted `Option`". This change just adds an implementation for visit_unit so the value will be parsed as None as expected. --- src/serde/visitor.rs | 4 +++ tests/integration/serde/mod.rs | 1 + tests/integration/serde/rfc2822.rs | 55 ++++++++++++++++++++++++++++++ tests/integration/serde/rfc3339.rs | 19 ++++++++++- 4 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 tests/integration/serde/rfc2822.rs diff --git a/src/serde/visitor.rs b/src/serde/visitor.rs index acf4d7c03..4e39ca038 100644 --- a/src/serde/visitor.rs +++ b/src/serde/visitor.rs @@ -299,6 +299,10 @@ macro_rules! well_known { fn visit_none(self) -> Result, E> { Ok(None) } + + fn visit_unit(self) -> Result { + Ok(None) + } } }; } diff --git a/tests/integration/serde/mod.rs b/tests/integration/serde/mod.rs index 1586aa9d8..61b3da60f 100644 --- a/tests/integration/serde/mod.rs +++ b/tests/integration/serde/mod.rs @@ -5,6 +5,7 @@ use time::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOf mod error_conditions; mod iso8601; mod macros; +mod rfc2822; mod rfc3339; mod timestamps; diff --git a/tests/integration/serde/rfc2822.rs b/tests/integration/serde/rfc2822.rs new file mode 100644 index 000000000..ef0580fc5 --- /dev/null +++ b/tests/integration/serde/rfc2822.rs @@ -0,0 +1,55 @@ +use serde::{Deserialize, Serialize}; +use serde_test::{assert_tokens, Configure, Token}; +use time::serde::rfc2822; +use time::OffsetDateTime; +use time_macros::datetime; + +#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] +struct Test { + #[serde(with = "rfc2822")] + dt: OffsetDateTime, + #[serde(with = "rfc2822::option")] + option_dt: Option, +} + +#[test] +fn serialize_deserialize() { + let value = Test { + dt: datetime!(2000-01-01 00:00:00 UTC), + option_dt: Some(datetime!(2000-01-01 00:00:00 UTC)), + }; + assert_tokens( + &value.compact(), + &[ + Token::Struct { + name: "Test", + len: 2, + }, + Token::Str("dt"), + Token::BorrowedStr("Sat, 01 Jan 2000 00:00:00 +0000"), + Token::Str("option_dt"), + Token::Some, + Token::BorrowedStr("Sat, 01 Jan 2000 00:00:00 +0000"), + Token::StructEnd, + ], + ); +} + +#[test] +fn parse_json() { + #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] + #[serde(untagged)] + enum Wrapper { + A(Test), + } + assert_eq!( + serde_json::from_str::( + r#"{"dt": "Sat, 01 Jan 2000 00:00:00 +0000", "option_dt": null}"# + ) + .unwrap(), + Wrapper::A(Test { + dt: datetime!(2000-01-01 00:00:00 UTC), + option_dt: None, + }) + ); +} diff --git a/tests/integration/serde/rfc3339.rs b/tests/integration/serde/rfc3339.rs index 49ee306d1..cb169b44f 100644 --- a/tests/integration/serde/rfc3339.rs +++ b/tests/integration/serde/rfc3339.rs @@ -15,7 +15,7 @@ struct Test { } #[test] -fn serialize() { +fn serialize_deserialize() { let value = Test { dt: datetime!(2000-01-01 00:00:00 UTC), option_dt: Some(datetime!(2000-01-01 00:00:00 UTC)), @@ -81,3 +81,20 @@ fn serialize() { "The offset_second component cannot be formatted into the requested format.", ); } + +#[test] +fn parse_json() { + #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] + #[serde(untagged)] + enum Wrapper { + A(Test), + } + assert_eq!( + serde_json::from_str::("{\"dt\": \"2000-01-01T00:00:00Z\", \"option_dt\": null}") + .unwrap(), + Wrapper::A(Test { + dt: datetime!(2000-01-01 00:00:00 UTC), + option_dt: None, + }) + ); +}