Fél*_*ger 4 rust serde serde-json
考虑这个Config
包含结构向量的结构Host
:
use serde::Deserialize;
use std::net::IpAddr;
#[derive(Debug, Deserialize)]
struct Config {
name: String,
hosts: Vec<Host>
}
#[derive(Debug, Deserialize)]
struct Host {
addr: IpAddr,
user: String,
}
Run Code Online (Sandbox Code Playgroud)
使用派生实现,可以使用和Deserialize
成功反序列化以下 JSON 和 YAML 配置文件:serde_json
serde_yaml
{
"name": "example",
"hosts": [
{ "addr": "1.1.1.1", "user": "alice" },
{ "addr": "2.2.2.2", "user": "bob" }
]
}
Run Code Online (Sandbox Code Playgroud)
---
name: example
hosts:
- addr: 1.1.1.1
user: alice
- addr: 2.2.2.2
user: bob
Run Code Online (Sandbox Code Playgroud)
但是,我还希望能够Host
从字符串反序列化结构。但是,重要的是我还可以从地图中反序列化它,并且理想情况下向量可以由两种格式组成。例如:
{
"name": "example",
"hosts": [
"alice@1.1.1.1",
{ "addr": "2.2.2.2", "user": "bob" }
]
}
Run Code Online (Sandbox Code Playgroud)
---
name: example
hosts:
- alice@1.1.1.1
- addr: 2.2.2.2
user: bob
Run Code Online (Sandbox Code Playgroud)
#[serde(try_from = "String")]
上面有Host
,我可以轻松支持字符串反序列化...但它不再反序列化地图格式。
serde 网站有一个关于反序列化字符串或结构的页面deserialize_with
,但它需要只能应用于字段(而不是结构容器)的属性。我不确定这种技术是否有效,因为我的领域是一个Vec<Host>
而不仅仅是一个Host
.
这有可能实现吗?
您可以为此使用未标记的枚举。与自定义解串器结合使用:
use std::str::FromStr;
use serde::{Deserialize, Deserializer};
use std::net::IpAddr;
#[derive(Debug, Deserialize)]
struct Config {
name: String,
hosts: Vec<Host>,
}
#[derive(Debug, Deserialize)]
struct InnerHost {
addr: IpAddr,
user: String,
}
#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum Host {
#[serde(deserialize_with = "deserialize_host_from_str")]
FromStr(InnerHost),
FromDict(InnerHost),
}
fn deserialize_host_from_str<'de, D>(deserializer: D) -> Result<InnerHost, D::Error>
where
D: Deserializer<'de>,
{
let value = String::deserialize(deserializer)?;
// parse the value and return host
Ok(InnerHost {
addr: IpAddr::from_str("1.1.1.1").unwrap(),
user: "foobar".to_string(),
})
}
fn main() {
let data = r#"{
"name": "example",
"hosts": [
"alice@1.1.1.1",
{ "addr": "2.2.2.2", "user": "bob" }
]
}"#;
let config : Config = serde_json::from_str(data).unwrap();
println!("{:?}", config);
}
Run Code Online (Sandbox Code Playgroud)
为了方便起见,您可以添加AsRef
for Host
to的 implInnerHost
或从枚举中提取它的方法。