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

如何将YAML的Map反序列化为带键对应name字段的自定义结构体Vec

问题描述

给定如下YAML内容,其中items是一个键值对映射结构:

items:
  item1:
    uid: ab1234
    foo: bar
  item2:
    uid: cd5678
    foo: baz

需要用Serde将其解析为Vec<Item>,其中Item的name字段由原映射的键(如item1、item2)填充,Item定义如下:

struct Item {
  name: String,  // 由映射键填充(item1, item2)
  uid: String,
  foo: String,
}

尝试了以下代码,但报错Err(Error("items: invalid type: map, expected a sequence", line: 3, column: 3)):

/*
[dependencies]
serde = "1.0.193"
serde_derive = "1.0.193"
serde_yaml = "0.9.27"
*/

use serde_derive::{Deserialize, Serialize};
use std::vec::Vec;


#[derive(Debug, Serialize, Deserialize)]
struct Item {
  name: String,
  uid: String,
  foo: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct Schema {
    items: Vec<Item>,
}

fn main() {
    let input = r###"
items:
  item1:
    uid: ab1234
    foo: bar
  item2:
    uid: cd5678
    foo: baz
"###;

    let items = serde_yaml::from_str::<Schema>(input);
    println!("{:?}", items);
}
解决方案

报错的核心原因是:YAML中的items是键值对映射类型,但代码里Schema的items字段定义为Vec<Item>(序列类型),Serde无法自动将映射转换为序列。要实现需求,需要手动处理映射到序列的转换,把映射的键赋值给Item的name字段。

下面提供两种可行的实现方式:

方式一:为Vec<Item>自定义反序列化逻辑

通过为Vec<Item>实现Deserialize trait,直接将YAML映射解析为目标数组:

use serde::{Deserialize, Deserializer};
use serde_derive::Deserialize;
use std::collections::HashMap;

#[derive(Debug, Deserialize)]
struct Item {
    #[serde(skip_deserializing)] // 跳过自动反序列化,后续手动填充
    name: String,
    uid: String,
    foo: String,
}

// 为Vec<Item>实现自定义反序列化逻辑
impl<'de> Deserialize<'de> for Vec<Item> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        // 先将YAML映射解析为HashMap<String, Item>
        let map = HashMap::<String, Item>::deserialize(deserializer)?;
        // 遍历HashMap,将键赋值给Item的name字段,最终收集为Vec
        Ok(map.into_iter()
            .map(|(name, mut item)| {
                item.name = name;
                item
            })
            .collect())
    }
}

#[derive(Debug, Deserialize)]
struct Schema {
    items: Vec<Item>,
}

fn main() {
    let input = r###"
items:
  item1:
    uid: ab1234
    foo: bar
  item2:
    uid: cd5678
    foo: baz
"###;

    let result = serde_yaml::from_str::<Schema>(input);
    println!("{:?}", result);
}

方式二:先解析为HashMap再手动转换

如果不想自定义反序列化逻辑,可以先把items解析成中间类型的HashMap,再转换为目标Vec<Item>:

use serde_derive::Deserialize;
use std::collections::HashMap;

// 中间类型:仅包含Item除name外的字段
#[derive(Debug, Deserialize)]
struct ItemRaw {
    uid: String,
    foo: String,
}

#[derive(Debug)]
struct Item {
    name: String,
    uid: String,
    foo: String,
}

#[derive(Debug, Deserialize)]
struct SchemaRaw {
    items: HashMap<String, ItemRaw>,
}

fn main() {
    let input = r###"
items:
  item1:
    uid: ab1234
    foo: bar
  item2:
    uid: cd5678
    foo: baz
"###;

    // 先解析为中间结构
    let raw_schema = serde_yaml::from_str::<SchemaRaw>(input).unwrap();
    // 转换为目标Vec<Item>
    let items: Vec<Item> = raw_schema.items
        .into_iter()
        .map(|(name, raw_item)| Item {
            name,
            uid: raw_item.uid,
            foo: raw_item.foo,
        })
        .collect();

    println!("{:?}", items);
}

两种方式都能实现需求:方式一更简洁,适合需要直接反序列化为目标类型的场景;方式二更直观,适合新手理解转换逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 13:08:18