您可以从映射或字符串反序列化结构吗?

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_jsonserde_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.

这有可能实现吗?

Net*_*ave 5

您可以为此使用未标记的枚举。与自定义解串器结合使用:

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)

操场

为了方便起见,您可以添加AsReffor Hostto的 implInnerHost或从枚举中提取它的方法。

  • 或者您可以使用 [`serde (from="HostEnum")`](https://serde.rs/container-attrs.html#from) 从枚举中自动提取主机 ([playground](https:// play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2018&amp;gist=cc5b79ddd98e882b9256b1300b81a49e)) (3认同)