使用 #[serde(untagged)] 和 #[serde(with)] 的组合反序列化枚举
Deserializing an enum using a combination of #[serde(untagged)] and #[serde(with)]
我正在尝试使用 actix-web 服务器作为通往小型堆栈的网关,以保证堆栈内部的数据格式严格,同时为用户提供一些自由。
为此,我想将 JSON 字符串反序列化为结构,然后对其进行验证、再次序列化并将其发布到消息代理上。数据的主要部分是包含整数、浮点数和日期时间的数组数组。我正在使用 serde 进行反序列化,使用 chrono 来处理日期时间。
我尝试将结构与枚举结合使用以允许不同的类型:
#[derive(Serialize, Deserialize)]
pub struct Data {
pub column_names: Option<Vec<String>>,
pub values: Vec<Vec<ValueType>>,
}
#[derive(Serialize, Deserialize)]
#[serde(untagged)]
pub enum ValueType {
I32(i32),
F64(f64),
#[serde(with = "datetime_handler")]
Dt(DateTime<Utc>),
}
由于 chrono::DateTime<T>
没有实现 Serialize
,我为此添加了一个自定义模块,类似于 in the serde docs.
的描述
mod datetime_handler {
use chrono::{DateTime, TimeZone, Utc};
use serde::{self, Deserialize, Deserializer, Serializer};
pub fn serialize<S>(dt: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let s = dt.to_rfc3339();
serializer.serialize_str(&s)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
where
D: Deserializer<'de>,
{
println!("Checkpoint 1");
let s = String::deserialize(deserializer)?;
println!("{}", s);
println!("Checkpoint 2");
let err1 = match DateTime::parse_from_rfc3339(&s) {
Ok(dt) => return Ok(dt.with_timezone(&Utc)),
Err(e) => Err(e),
};
println!("Checkpoint 3");
const FORMAT1: &'static str = "%Y-%m-%d %H:%M:%S";
match Utc.datetime_from_str(&s, FORMAT1) {
Ok(dt) => return Ok(dt.with_timezone(&Utc)),
Err(e) => println!("{}", e), // return first error not second if both fail
};
println!("Checkpoint 4");
return err1.map_err(serde::de::Error::custom);
}
}
这会依次尝试 2 种不同的时间格式,并且适用于 DateTime 字符串。
问题
`#[derive(Serialize, Deserialize)]`、`#[serde(untagged)]` 和 `#[serde(with)]` 的组合似乎做了一些意想不到的事情。 `serde:from_str(...)` 尝试使用我自定义的 `deserialize` 函数反序列化数组中的每个条目。
我希望它首先尝试反序列化为 ValueType::I32,成功并继续下一个条目,如 [the docs](https://serde.rs/enum-representations.html)说:
Serde will try to match the data against each variant in order and the first one that deserializes successfully is the one returned.
发生的事情是自定义 deserialize
应用于例如"0"
失败,反序列化停止。
这是怎么回事?我该如何解决?
我的想法是我无法以错误的方式反序列化,或者我以某种方式“覆盖”了派生的反序列化。
许多 crate 将实现 serde
和其他通用实用程序 crate,但会将它们保留为可选功能。这有助于节省编译时间。您可以通过查看 Cargo.toml 文件来检查一个 crate,看看是否有它的功能或依赖项已包含但标记为可选。
对于你的情况,我可以转到 chrono
on crates.io and select the Repository link to view the source code for the crate. In the Cargo.toml
文件,我可以看到使用了 serde,但默认情况下未启用。
[features]
default = ["clock", "std", "oldtime"]
alloc = []
std = []
clock = ["libc", "std", "winapi"]
oldtime = ["time"]
wasmbind = ["wasm-bindgen", "js-sys"]
unstable-locales = ["pure-rust-locales", "alloc"]
__internal_bench = []
__doctest = []
[depenencies]
...
serde = { version = "1.0.99", default-features = false, optional = true }
要启用它,您可以进入项目的 Cargo.toml
并将其作为功能添加到 chrono
。
[depenencies]
chrono = { version: "0.4.19", features = ["serde"] }
或者,chrono
在 documentation 中列出了一些(但不是全部?)可选功能。然而,并不是所有的箱子都这样做,而且文档有时会过时,所以我通常更喜欢手动方法。
至于 deserialize_with
和 untagged
在枚举上的交互问题,我看不出你的代码有任何问题。这可能是 serde 中的一个错误,所以我建议您在 serde Repository 上创建一个问题,以便他们可以进一步调查为什么会出现此错误。
@jonasbb 帮助我意识到代码在使用 [0,16.9,"2020-12-23 00:23:14"]
时有效,但在尝试反序列化 ["0","16.9","2020-12-23 00:23:14"]
时却无效。默认情况下,Serde 不会序列化字符串中的数字,对 I32 和 F64 的尝试只会默默地失败。这在这个 serde-issue and can be solved using the inofficial serde-aux crate 中讨论。
我正在尝试使用 actix-web 服务器作为通往小型堆栈的网关,以保证堆栈内部的数据格式严格,同时为用户提供一些自由。
为此,我想将 JSON 字符串反序列化为结构,然后对其进行验证、再次序列化并将其发布到消息代理上。数据的主要部分是包含整数、浮点数和日期时间的数组数组。我正在使用 serde 进行反序列化,使用 chrono 来处理日期时间。
我尝试将结构与枚举结合使用以允许不同的类型:
#[derive(Serialize, Deserialize)]
pub struct Data {
pub column_names: Option<Vec<String>>,
pub values: Vec<Vec<ValueType>>,
}
#[derive(Serialize, Deserialize)]
#[serde(untagged)]
pub enum ValueType {
I32(i32),
F64(f64),
#[serde(with = "datetime_handler")]
Dt(DateTime<Utc>),
}
由于 chrono::DateTime<T>
没有实现 Serialize
,我为此添加了一个自定义模块,类似于 in the serde docs.
mod datetime_handler {
use chrono::{DateTime, TimeZone, Utc};
use serde::{self, Deserialize, Deserializer, Serializer};
pub fn serialize<S>(dt: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let s = dt.to_rfc3339();
serializer.serialize_str(&s)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
where
D: Deserializer<'de>,
{
println!("Checkpoint 1");
let s = String::deserialize(deserializer)?;
println!("{}", s);
println!("Checkpoint 2");
let err1 = match DateTime::parse_from_rfc3339(&s) {
Ok(dt) => return Ok(dt.with_timezone(&Utc)),
Err(e) => Err(e),
};
println!("Checkpoint 3");
const FORMAT1: &'static str = "%Y-%m-%d %H:%M:%S";
match Utc.datetime_from_str(&s, FORMAT1) {
Ok(dt) => return Ok(dt.with_timezone(&Utc)),
Err(e) => println!("{}", e), // return first error not second if both fail
};
println!("Checkpoint 4");
return err1.map_err(serde::de::Error::custom);
}
}
这会依次尝试 2 种不同的时间格式,并且适用于 DateTime 字符串。
问题
`#[derive(Serialize, Deserialize)]`、`#[serde(untagged)]` 和 `#[serde(with)]` 的组合似乎做了一些意想不到的事情。 `serde:from_str(...)` 尝试使用我自定义的 `deserialize` 函数反序列化数组中的每个条目。 我希望它首先尝试反序列化为 ValueType::I32,成功并继续下一个条目,如 [the docs](https://serde.rs/enum-representations.html)说:Serde will try to match the data against each variant in order and the first one that deserializes successfully is the one returned.
发生的事情是自定义 deserialize
应用于例如"0"
失败,反序列化停止。
这是怎么回事?我该如何解决?
我的想法是我无法以错误的方式反序列化,或者我以某种方式“覆盖”了派生的反序列化。
许多 crate 将实现 serde
和其他通用实用程序 crate,但会将它们保留为可选功能。这有助于节省编译时间。您可以通过查看 Cargo.toml 文件来检查一个 crate,看看是否有它的功能或依赖项已包含但标记为可选。
对于你的情况,我可以转到 chrono
on crates.io and select the Repository link to view the source code for the crate. In the Cargo.toml
文件,我可以看到使用了 serde,但默认情况下未启用。
[features]
default = ["clock", "std", "oldtime"]
alloc = []
std = []
clock = ["libc", "std", "winapi"]
oldtime = ["time"]
wasmbind = ["wasm-bindgen", "js-sys"]
unstable-locales = ["pure-rust-locales", "alloc"]
__internal_bench = []
__doctest = []
[depenencies]
...
serde = { version = "1.0.99", default-features = false, optional = true }
要启用它,您可以进入项目的 Cargo.toml
并将其作为功能添加到 chrono
。
[depenencies]
chrono = { version: "0.4.19", features = ["serde"] }
或者,chrono
在 documentation 中列出了一些(但不是全部?)可选功能。然而,并不是所有的箱子都这样做,而且文档有时会过时,所以我通常更喜欢手动方法。
至于 deserialize_with
和 untagged
在枚举上的交互问题,我看不出你的代码有任何问题。这可能是 serde 中的一个错误,所以我建议您在 serde Repository 上创建一个问题,以便他们可以进一步调查为什么会出现此错误。
@jonasbb 帮助我意识到代码在使用 [0,16.9,"2020-12-23 00:23:14"]
时有效,但在尝试反序列化 ["0","16.9","2020-12-23 00:23:14"]
时却无效。默认情况下,Serde 不会序列化字符串中的数字,对 I32 和 F64 的尝试只会默默地失败。这在这个 serde-issue and can be solved using the inofficial serde-aux crate 中讨论。