使用泛型类型反序列化可选的struct字段会导致语义怪异

nev*_*fox 8 rust serde

我试图将JSON反序列化为包含可选字段的结构authorization.JSON可能包含也可能不包含此字段.如果确实包含该字段,我正在进行自定义反序列化hyper::header::Authorization<hyper::header::Scheme>.因为Authorization需要泛型类型Scheme,我需要(正如我写的那样)在我的struct上包含泛型类型.

所有的测试都通过了,但最后一个(没有授权字段的de_json_noneJSON )是语义上的怪异,因为我必须使用一个明确的类型(如图所示或者)来定位变量,这两个都没有任何意义.数据,尽管从Rust的角度来看完全有效.SchemeBearerBasic

很清楚为什么会这样,但这是我不想要的东西,我不知道如何修复.

我想编写一个Rocket处理程序,它只Authorization<Bearer>通过设置数据类型来匹配包含类型授权字段的数据Headers<Bearer>.目前,它还会匹配根本没有该字段的数据.我也陷入困境,没有明确的方法来特别按类型调用缺少字段的数据.

我在寻找如何重构这个代码,以反映这一事实的建议Headers确实有三个不同的,相互排斥的化身(Basic,BearerNone).也许我应该想在这里做点什么?

extern crate hyper;
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;

use hyper::header::{Authorization, Header, Raw, Scheme};
use serde::{Deserialize, Deserializer};

#[derive(Debug, Deserialize, PartialEq)]
struct Headers<S>
where
    S: Scheme + 'static,
{
    #[serde(deserialize_with = "auth_header", default = "no_auth")]
    authorization: Option<Authorization<S>>,
    #[serde(rename = ":path")]
    path: String,
}

fn auth_header<'de, D, S>(deserializer: D) -> Result<Option<Authorization<S>>, D::Error>
where
    D: Deserializer<'de>,
    S: Scheme + 'static,
{
    let s = String::deserialize(deserializer)?;
    let auth = Authorization::parse_header(&Raw::from(s.into_bytes()));
    auth.map(|a| Some(a)).map_err(serde::de::Error::custom)
}

fn no_auth<S>() -> Option<Authorization<S>>
where
    S: Scheme + 'static,
{
    None
}

#[cfg(test)]
mod test {
    use hyper::header::{Basic, Bearer};
    use serde_json;
    use super::*;

    #[test]
    fn de_json_basic() {
        let data = r#"{
                        "authorization": "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==",
                        ":path": "/service/",
                        ":method": "GET"
                      }"#;

        let message = Headers {
            authorization: Some(Authorization(Basic {
                username: "Aladdin".to_owned(),
                password: Some("open sesame".to_owned()),
            })),
            path: "/service/".to_owned(),
        };

        let h: Headers<Basic> = serde_json::from_str(data).unwrap();

        assert_eq!(message, h);
    }

    #[test]
    fn de_json_bearer() {
        let data = r#"{
                        "authorization": "Bearer fpKL54jvWmEGVoRdCNjG",
                        ":path": "/service/",
                        ":method": "GET"
                      }"#;

        let message = Headers {
            authorization: Some(Authorization(
                Bearer { token: "fpKL54jvWmEGVoRdCNjG".to_owned() },
            )),
            path: "/service/".to_owned(),
        };

        let h: Headers<Bearer> = serde_json::from_str(data).unwrap();

        assert_eq!(message, h);
    }

    #[test]
    fn de_json_none() {
        let data = r#"{
                        ":path": "/service/",
                        ":method": "GET"
                      }"#;

        let message = Headers {
            authorization: None,
            path: "/service/".to_owned(),
        };

        let h: Headers<Bearer> = serde_json::from_str(data).unwrap();
        // this also works, though neither should ideally
        // let h: Headers<Basic> = serde_json::from_str(data).unwrap();

        assert_eq!(message, h);
    }
}
Run Code Online (Sandbox Code Playgroud)

She*_*ter 3

None没有相应的Sometype就没有 a 的概念编译器需要知道为任一情况的值分配多少空间:

\n\n
struct ReallyBig([u8; 1024]);\nstruct ReallySmall(u8);\n\nfn main() {\n    let mut choice = None; // How much space to allocate?\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

在您的代码中, 的大小Authorization可以取决于为 选择的值S。既然Headers包含一个Option<Authorization<S>>, 的大小Headers 可以取决于 的选择S

\n\n

即使您没有获得任何值,您也必须选择解析为某种特定类型。也许您稍后会通过构建适当的值 \xe2\x80\x94 来手动将其从 a 更改None为 a Some,如果没有分配足够的空间,那就很麻烦了!

\n\n

因此,我看不到您的解决方案将如何工作。类型是静态的 \xe2\x80\x94 你需要在编译时知道解码 JSON 是否会导致Authorizationor Bearer,而这是不可能的。

\n\n

通常,我建议您使用带有Box<Scheme>. 这在这里不起作用,因为Scheme不是对象安全的。

\n\n

然后,我建议您实现自己的枚举包装,Basic或者Box并实现Scheme它。这并不容易工作,因为Scheme::scheme必须返回单个关键字,但您实际上支持两个关键字!

\n\n

下一步是实现我们自己的Header

\n\n
extern crate hyper;\nextern crate serde;\nextern crate serde_json;\n#[macro_use]\nextern crate serde_derive;\n\nuse hyper::header::{Authorization, Header, Raw, Basic, Bearer};\nuse serde::{Deserialize, Deserializer};\nuse std::fmt;\n\n#[derive(Debug, Clone, PartialEq)]\nenum MyAuthorization {\n    Basic(Authorization<Basic>),\n    Bearer(Authorization<Bearer>),\n}\n\nimpl Header for MyAuthorization {\n    fn header_name() -> &\'static str {\n        // Should always be the same header name, right?\n        Authorization::<Basic>::header_name()\n    }\n\n    fn parse_header(raw: &Raw) -> hyper::error::Result<Self> {\n        Authorization::<Basic>::parse_header(raw)\n            .map(MyAuthorization::Basic)\n            .or_else(|_| {\n                Authorization::<Bearer>::parse_header(raw).map(MyAuthorization::Bearer)\n            })\n    }\n\n    fn fmt_header(&self, f: &mut hyper::header::Formatter) -> fmt::Result {\n        match *self {\n            MyAuthorization::Basic(ref a) => a.fmt_header(f),\n            MyAuthorization::Bearer(ref a) => a.fmt_header(f),\n        }\n    }\n}\n\n#[derive(Debug, Deserialize, PartialEq)]\nstruct Headers {\n    #[serde(deserialize_with = "auth_header", default)]\n    authorization: Option<MyAuthorization>,\n    #[serde(rename = ":path")]\n    path: String,\n}\n\nfn auth_header<\'de, D>(deserializer: D) -> Result<Option<MyAuthorization>, D::Error>\nwhere\n    D: Deserializer<\'de>,\n{\n    let s = String::deserialize(deserializer)?;\n    let auth = MyAuthorization::parse_header(&Raw::from(s.into_bytes()));\n    auth.map(Some).map_err(serde::de::Error::custom)\n}\n\n#[cfg(test)]\nmod test {\n    use hyper::header::{Basic, Bearer};\n    use serde_json;\n    use super::*;\n\n    #[test]\n    fn de_json_basic() {\n        let data = r#"{\n                        "authorization": "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==",\n                        ":path": "/service/",\n                        ":method": "GET"\n                      }"#;\n\n        let message = Headers {\n            authorization: Some(MyAuthorization::Basic(Authorization(Basic {\n                username: "Aladdin".to_owned(),\n                password: Some("open sesame".to_owned()),\n            }))),\n            path: "/service/".to_owned(),\n        };\n\n        let h: Headers = serde_json::from_str(data).unwrap();\n\n        assert_eq!(message, h);\n    }\n\n    #[test]\n    fn de_json_bearer() {\n        let data = r#"{\n                        "authorization": "Bearer fpKL54jvWmEGVoRdCNjG",\n                        ":path": "/service/",\n                        ":method": "GET"\n                      }"#;\n\n        let message = Headers {\n            authorization: Some(MyAuthorization::Bearer(Authorization(\n                Bearer { token: "fpKL54jvWmEGVoRdCNjG".to_owned() },\n            ))),\n            path: "/service/".to_owned(),\n        };\n\n        let h: Headers = serde_json::from_str(data).unwrap();\n\n        assert_eq!(message, h);\n    }\n\n    #[test]\n    fn de_json_none() {\n        let data = r#"{\n                        ":path": "/service/",\n                        ":method": "GET"\n                      }"#;\n\n        let message = Headers {\n            authorization: None,\n            path: "/service/".to_owned(),\n        };\n\n        let h: Headers = serde_json::from_str(data).unwrap();\n\n        assert_eq!(message, h);\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

您可能希望咨询 Hyper 维护者,看看这是否是执行此类操作的预期方式。

\n