use serde_derive::Deserialize; // 1.0.118
use toml; // 0.5.8
#[derive(Debug, Deserialize)]
struct Remote {
enabled: bool,
address: String,
}
#[derive(Debug, Deserialize)]
struct Config {
remote: Option<Remote>,
}
fn main() {
let config: Config = toml::from_str(r#"
[remote]
enabled = true
address = "example.com"
"#).unwrap();
println!("{:?}", config);
let config: Config = toml::from_str(r#"
[remote]
enabled = false
address = "example.com"
"#).unwrap();
println!("{:?}", config);
// How to make `config.remote` `None` ?
}
Run Code Online (Sandbox Code Playgroud)
我希望 的其余字段Remote
是可选的,并且Config::remote
当设置为 falseNone
时。enabled
我尝试了tag
属性,但没有按预期工作。
不管你相信与否,您实际上可以使用该#[serde(deserialize_with = "...")]
属性来做到这一点。它允许您覆盖应用于特定字段的反序列化器。这里的目标是使用一个覆盖的反序列化器来调用常规反序列化器,然后手动检查启用的字段。serde 网站上有(薄弱的)记录。
首先,代码:
// Important: Deserialize is needed to access T::deserialize() methods.
use serde::{Deserialize, Deserializer};
fn none_if_disabled<'de, D>(deserializer: D) -> Result<Option<Remote>, D::Error>
where
D: Deserializer<'de>,
{
let remote = Option::<Remote>::deserialize(deserializer)?;
Ok(remote.and_then(|r| if r.enabled { Some(r) } else { None }))
}
#[derive(Debug, Deserialize)]
struct Remote {
enabled: bool,
address: String,
}
#[derive(Debug, Deserialize)]
struct Config {
#[serde(deserialize_with = "none_if_disabled")]
remote: Option<Remote>,
}
Run Code Online (Sandbox Code Playgroud)
让我们分解一下。如果您不熟悉 serde 的内部结构,函数签名会有点粗糙。
fn none_if_disabled<'de, D>(deserializer: D) -> Result<Option<Remote>, D::Error>
where
D: Deserializer<'de>,
Run Code Online (Sandbox Code Playgroud)
D
是实现的东西Deserializer
。这通常由解析器箱提供,在本例中为toml,尽管其他解析器也有自己的。我们可以要求D
尝试为我们提供各种原语,或者我们可以将其传递给实现的东西Deserialize
,这将为我们消耗这些原语。我们需要返回成功解析的值 ( Option<Remote>
) 或冒出我们收到的任何错误。
在函数内部,第一步是使用Deserialize
我们从#[derive(Deserialize)]
on获得的常规 impl Remote
。这是“正常”解析步骤,如果我们没有属性,则大致会这样调用deserialize_with
。
let remote = Option::<Remote>::deserialize(deserializer)?;
Run Code Online (Sandbox Code Playgroud)
然后,我们需要过滤逻辑。只要它输出一个Result<Option<Remote>, _>
.
Ok(remote.and_then(|r| if r.enabled { Some(r) } else { None }))
Run Code Online (Sandbox Code Playgroud)
最后,我们需要告诉 serde 我们精美的解串器。
#[serde(deserialize_with = "none_if_disabled")]
Run Code Online (Sandbox Code Playgroud)
您的函数的输出main
是:
Config { remote: Some(Remote { enabled: true, address: "example.com" }) }
Config { remote: None }
Run Code Online (Sandbox Code Playgroud)
这是可行的,但只会带来一些额外的复杂性。@Skarlett 是完全正确的,serde 并不是真正为这种配置管理而构建的,并且您可能最好对 serde 直接为您解析的内容进行后处理传递。我的首选方法是保留所有Remote
s 并根据标志显式更改行为enabled
- 这允许您使用禁用的配置执行操作,例如显式记录未尝试连接的情况。
也就是说,serde 的这些用途确实有一席之地,即使配置文件可能不是。
归档时间: |
|
查看次数: |
1370 次 |
最近记录: |