You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用serde-yaml将YAML键值对解析为指定Rust结构体?

问题:将动态键的YAML反序列化为Rust结构体

需要解析的YAML内容:

networks:
  foo1: 192.168.1.0/24
  bar1: 192.168.2.0/24
  foo2: 2001:CAFE:1::/64
  bar2: 2001:CAFE:2::/64

目标Rust结构体:

#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Networks {
    networks: Vec<Network>
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Network {
    name: String,
    subnet: String,
}

偏好当前YAML的简洁写法,而非冗余的列表式结构,但现有方法仅能将网络条目解析为HashMap或元组向量,无法直接映射到带命名字段的Network结构体。


解决方案:自定义Serde反序列化逻辑

通过为Networks结构体实现自定义Deserialize trait,可直接将YAML中的动态键值对转换为Network实例的向量,无需额外转换步骤。

步骤1:添加依赖

在Cargo.toml中声明必要依赖:

[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.9"

步骤2:实现自定义反序列化逻辑

use serde::de::{MapAccess, Visitor};
use serde::{Deserialize, Deserializer};
use std::fmt;

#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Networks {
    networks: Vec<Network>,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Network {
    name: String,
    subnet: String,
}

// 为Networks实现自定义反序列化
impl<'de> Deserialize<'de> for Networks {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        // 定义Visitor处理网络条目映射
        struct NetworksVisitor;

        impl<'de> Visitor<'de> for NetworksVisitor {
            type Value = Vec<Network>;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("a map of network names to subnets")
            }

            fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
            where
                M: MapAccess<'de>,
            {
                let mut networks = Vec::new();
                while let Some((name, subnet)) = map.next_entry()? {
                    networks.push(Network { name, subnet });
                }
                Ok(networks)
            }
        }

        // 定义外层结构,指定用自定义逻辑解析networks字段
        #[derive(Deserialize)]
        struct Outer {
            #[serde(deserialize_with = "deserialize_networks")]
            networks: Vec<Network>,
        }

        fn deserialize_networks<'de, D>(deserializer: D) -> Result<Vec<Network>, D::Error>
        where
            D: Deserializer<'de>,
        {
            deserializer.deserialize_map(NetworksVisitor)
        }

        let outer = Outer::deserialize(deserializer)?;
        Ok(Networks { networks: outer.networks })
    }
}

// 测试验证
fn main() -> Result<(), serde_yaml::Error> {
    let yaml = r#"
networks:
  foo1: 192.168.1.0/24
  bar1: 192.168.2.0/24
  foo2: 2001:CAFE:1::/64
  bar2: 2001:CAFE:2::/64
"#;

    let networks: Networks = serde_yaml::from_str(yaml)?;
    println!("{:?}", networks);

    // 验证解析结果正确性
    assert_eq!(
        networks.networks,
        vec![
            Network { name: "foo1".to_string(), subnet: "192.168.1.0/24".to_string() },
            Network { name: "bar1".to_string(), subnet: "192.168.2.0/24".to_string() },
            Network { name: "foo2".to_string(), subnet: "2001:CAFE:1::/64".to_string() },
            Network { name: "bar2".to_string(), subnet: "2001:CAFE:2::/64".to_string() },
        ]
    );

    Ok(())
}

逻辑说明

  • 自定义NetworksVisitor遍历YAML中的键值对,将每个键作为name字段、对应值作为subnet字段,构建Network实例并收集为向量。
  • 通过serde(deserialize_with)属性,指定用自定义逻辑处理networks字段的反序列化流程。
  • 既保留了偏好的简洁YAML写法,又直接映射到带命名字段的结构体,避免额外转换操作。

内容的提问来源于stack exchange,提问作者Giftzwerg02

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 07:03:18