我试图将JSON反序列化为包含可选字段的结构authorization.JSON可能包含也可能不包含此字段.如果确实包含该字段,我正在进行自定义反序列化hyper::header::Authorization<hyper::header::Scheme>.因为Authorization需要泛型类型Scheme,我需要(正如我写的那样)在我的struct上包含泛型类型.
所有的测试都通过了,但最后一个(没有授权字段的de_json_noneJSON )是语义上的怪异,因为我必须使用一个明确的类型(如图所示或者)来定位变量,这两个都没有任何意义.数据,尽管从Rust的角度来看完全有效.SchemeBearerBasic
很清楚为什么会这样,但这是我不想要的东西,我不知道如何修复.
我想编写一个Rocket处理程序,它只Authorization<Bearer>通过设置数据类型来匹配包含类型授权字段的数据Headers<Bearer>.目前,它还会匹配根本没有该字段的数据.我也陷入困境,没有明确的方法来特别按类型调用缺少字段的数据.
我在寻找如何重构这个代码,以反映这一事实的建议Headers确实有三个不同的,相互排斥的化身(Basic,Bearer和None).也许我应该想在这里做点什么?
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)
None没有相应的Sometype就没有 a 的概念。编译器需要知道为任一情况的值分配多少空间:
struct ReallyBig([u8; 1024]);\nstruct ReallySmall(u8);\n\nfn main() {\n let mut choice = None; // How much space to allocate?\n}\nRun Code Online (Sandbox Code Playgroud)\n\n在您的代码中, 的大小Authorization可以取决于为 选择的值S。既然Headers包含一个Option<Authorization<S>>, 的大小Headers 也可以取决于 的选择S。
即使您没有获得任何值,您也必须选择解析为某种特定类型。也许您稍后会通过构建适当的值 \xe2\x80\x94 来手动将其从 a 更改None为 a Some,如果没有分配足够的空间,那就很麻烦了!
因此,我看不到您的解决方案将如何工作。类型是静态的 \xe2\x80\x94 你需要在编译时知道解码 JSON 是否会导致Authorizationor Bearer,而这是不可能的。
通常,我建议您使用带有Box<Scheme>. 这在这里不起作用,因为Scheme不是对象安全的。
然后,我建议您实现自己的枚举包装,Basic或者Box并实现Scheme它。这并不容易工作,因为Scheme::scheme必须返回单个关键字,但您实际上支持两个关键字!
下一步是实现我们自己的Header:
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}\nRun Code Online (Sandbox Code Playgroud)\n\n您可能希望咨询 Hyper 维护者,看看这是否是执行此类操作的预期方式。
\n