【问题标题】:Can you deserialize a struct from a map or a string?您可以从映射或字符串反序列化结构吗?
【发布时间】:2021-10-14 04:24:38
【问题描述】:

考虑这个包含Host 结构向量的Config 结构:

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,
}

使用派生的Deserialize 实现,可以使用serde_json 和serde_yaml 成功反序列化以下JSON 和YAML 配置文件:

{
  "name": "example",
  "hosts": [
    { "addr": "1.1.1.1", "user": "alice" },
    { "addr": "2.2.2.2", "user": "bob" }
  ]
}
---
name: example
hosts:
  - addr: 1.1.1.1
    user: alice
  - addr: 2.2.2.2
    user: bob

但是,我还希望能够从字符串中反序列化 Host 结构。但是,重要的是我还可以从地图反序列化它,理想情况下,矢量可以由两种格式组成。例如:

{
  "name": "example",
  "hosts": [
    "alice@1.1.1.1",
    { "addr": "2.2.2.2", "user": "bob" }
  ]
}
---
name: example
hosts:
  - alice@1.1.1.1
  - addr: 2.2.2.2
    user: bob

在Host 结构之上使用#[serde(try_from = "String")],我可以轻松支持字符串反序列化...但是它不再反序列化映射格式。

serde 网站有一个 page 关于反序列化字符串或结构,但它需要 deserialize_with 属性,该属性只能应用于字段(而不是结构容器)。我不确定这种技术是否有效,因为我的领域是 Vec&lt;Host&gt; 而不仅仅是 Host。

这有可能实现吗?

【问题讨论】:

标签: rust serde serde-json


【解决方案1】:

您可以为此使用未标记的枚举。结合自定义反序列化器:

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);
}

Playground

为方便起见,您可以将 Host 的 AsRef impl 添加到 InnerHost 或从枚举中提取它的方法。

【讨论】:

  • 或者你可以使用serde (from="HostEnum")从枚举中自动提取主机(playground)
  • @Jmb,不错的补充!谢谢!
  • 很好的解决方案!谢谢你们两个:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-19
  • 1970-01-01
  • 2021-04-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多