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

如何使用serde_yaml将含任意键的YAML反序列化到Rust自定义结构体

方案1:不改动现有YAML结构

你之前的思路里将数组元素映射为BTreeMap<String, Node>不符合YAML的实际结构:每个数组元素中,动态键(如G1、Sn1)对应的值是字符串类型的节点名称,supportedBy是和动态键平级的字段。我们可以通过serde的#[serde(flatten)]属性捕获动态的ID-名称键值对,调整后的代码如下:

use indoc::indoc;
use std::collections::BTreeMap;
use serde::{Serialize, Deserialize};

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct NodeEntry {
    supported_by: Option<Vec<String>>,
    // 捕获所有未匹配到结构体字段的键值对,也就是动态ID和名称的映射
    #[serde(flatten)]
    id_name: BTreeMap<String, String>,
}

impl NodeEntry {
    // 辅助方法快速获取节点ID
    pub fn id(&self) -> &str {
        self.id_name.keys().next().unwrap()
    }
    // 辅助方法快速获取节点名称
    pub fn name(&self) -> &str {
        self.id_name.values().next().unwrap()
    }
}

fn main() -> Result<(), serde_yaml::Error> {
    let test_yaml = indoc! {"
            ---
            - G1: \"Test\"
              supportedBy: [Sn1]
            - Sn1: \"Lala\"
            "};

    let deser: Vec<NodeEntry> = serde_yaml::from_str(&test_yaml)?;
    println!("第一个节点ID:{},名称:{},依赖:{:?}", deser[0].id(), deser[0].name(), deser[0].supported_by);
    println!("第二个节点ID:{},名称:{},依赖:{:?}", deser[1].id(), deser[1].name(), deser[1].supported_by);
    Ok(())
}

运行输出如下:

第一个节点ID:G1,名称:Test,依赖:Some(["Sn1"])
第二个节点ID:Sn1,名称:Lala,依赖:None

方案2:微调YAML结构(推荐)

如果可以接受极小的YAML结构调整,我们可以让结构更清晰,也避免每次取ID和名称都要遍历Map的开销:
调整后的YAML:

---
- id: G1
  name: "Test"
  supportedBy: [Sn1]
- id: Sn1
  name: "Lala"

对应结构体可以直接写为:

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct Node {
    id: String,
    name: String,
    supported_by: Option<Vec<String>>,
}

直接解析为Vec<Node>即可,人工编写的复杂度几乎没有提升,结构可读性也更好。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.12 13:57:04