首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用#[serde(无标记)]和#[serde(with)]的组合反序列化枚举

使用#[serde(无标记)]和#[serde(with)]的组合反序列化枚举
EN

Stack Overflow用户
提问于 2021-07-09 09:39:29
回答 2查看 1.4K关注 0票数 3

我试图使用actix服务器作为通向小堆栈的网关,以保证堆栈内有严格的数据格式,同时允许用户有一些自由。

为此,我希望将JSON字符串反序列化到结构中,然后验证它,再次序列化它,并在message上发布它。数据的主要部分是包含整数、浮点数和日期时间的数组数组。我使用serde进行反序列化,并使用chrono来处理日期时间。

我尝试使用结构和枚举相结合来允许不同的类型:

代码语言:javascript
复制
#[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,所以我为该模块添加了一个自定义模块,类似于如何描述在农庄的医生

代码语言:javascript
复制
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);
    }
}

这将一个接一个地尝试两种不同的时间格式,并适用于DateTime字符串。

问题

#[derive(Serialize, Deserialize)]#[serde(untagged)]#[serde(with)]的结合似乎做了一些意想不到的事情。serde:from\_str(...)试图用我的自定义deserialize函数反序列化数组中的每个条目。我希望它要么尝试先反序列化为ValueType::I32,然后再继续下一个条目,就像医生们说的:

Serde将尝试按照顺序将数据与每个变体匹配,第一个反序列化成功的是返回的变量。

所发生的情况是将自定义deserialize应用于例如"0"失败和反序列化停止。

到底怎么回事?我该怎么解决呢?

我的想法是,我要么没有以错误的方式反序列化,要么我用自己的方法“覆盖”派生反序列化。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-07-09 12:41:42

@jonasbb帮助我认识到代码在使用[0,16.9,"2020-12-23 00:23:14"]时工作,但在试图反序列化["0","16.9","2020-12-23 00:23:14"]时却不起作用。默认情况下,Serde不序列化字符串中的数字,I32和F64的尝试只是默默地失败了。这是在这个塞德问题中讨论的,可以用非正式的塞德-奥克斯机箱来解决。

票数 2
EN

Stack Overflow用户

发布于 2021-07-09 10:43:02

许多板条箱将实现serde和其他公共实用程序板条箱,但它们将保留为可选特性。这有助于在编译时节省时间。您可以通过查看Cargo.toml文件来检查机箱,查看它是否有特性或是否包含依赖项,但将其标记为可选。

在您的例子中,我可以转到chrono on crates.io并选择存储库链接来查看机箱的源代码。在Cargo.toml文件中,我可以看到使用了serde,但默认情况下没有启用。

代码语言:javascript
复制
[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中。

代码语言:javascript
复制
[depenencies]
chrono = { version: "0.4.19", features = ["serde"] }

或者,chrono列出了一些(但不是全部?)它们在文档中的可选特性。然而,并不是所有的板条箱都这样做,而且文档有时会过时,所以我通常更喜欢手动方法。

至于枚举上的deserialize_with untagged untagged之间的交互问题,我认为您的代码没有任何问题。这可能是服务器中的一个bug,所以我建议您在 serde储存库 上创建一个问题,这样他们就可以进一步研究为什么会发生此错误。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68314646

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档