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

如何获取Serde与serde-yaml解析字段时的行/字符位置?

用Serde + serde-yaml获取YAML字段的行号与字符位置

要实现解析YAML时捕获每个字段的原始行号(及可选字符位置),核心是利用serde-yaml提供的位置访问能力,通过自定义反序列化包装类型实现。

实现步骤

  1. 定义带位置的包装结构体
    创建泛型结构体存储字段值与对应位置信息:
use serde::de::{self, Deserialize, Deserializer};
use serde_yaml::Location;

#[derive(Debug)]
struct WithPos<T> {
    pub value: T,
    pub line: usize,
    pub column: usize, // 可选字符位置,不需要可移除
}
  1. 为包装体实现自定义反序列化
    在反序列化流程中捕获当前位置,同时解析字段实际值:
impl<T> Deserialize for WithPos<T>
where
    T: Deserialize<'de>,
{
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        // 获取当前解析位置
        let location = Location::new(deserializer)
            .map_err(de::Error::custom)?;
        
        // 反序列化字段值
        let value = T::deserialize(deserializer)?;

        Ok(WithPos {
            value,
            line: location.line(),
            column: location.column(),
        })
    }
}

// 针对嵌套映射的适配:自动为每个键值对添加位置
#[derive(Debug, Deserialize)]
struct PosMapping(#[serde(deserialize_with = "deserialize_pos_mapping")] pub std::collections::HashMap<String, WithPos<serde_yaml::Value>>);

fn deserialize_pos_mapping<'de, D>(deserializer: D) -> Result<std::collections::HashMap<String, WithPos<serde_yaml::Value>>, D::Error>
where
    D: Deserializer<'de>,
{
    use serde::de::MapAccess;

    struct PosMapVisitor;

    impl<'de> de::Visitor<'de> for PosMapVisitor {
        type Value = std::collections::HashMap<String, WithPos<serde_yaml::Value>>;

        fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
            formatter.write_str("a YAML mapping")
        }

        fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
        where
            M: MapAccess<'de>,
        {
            let mut result = std::collections::HashMap::new();
            while let Some((key, value)) = map.next_entry::<String, WithPos<serde_yaml::Value>>()? {
                result.insert(key, value);
            }
            Ok(result)
        }
    }

    deserializer.deserialize_map(PosMapVisitor)
}
  1. 解析示例YAML并输出结果
    用上述结构解析目标YAML:
fn main() -> Result<(), Box<dyn std::error::Error>> {
    let yaml_str = r#"
my_first_field: 0
my_second_field:
  nested: test
"#;

    let pos_mapping: PosMapping = serde_yaml::from_str(yaml_str)?;
    println!("{:#?}", pos_mapping);

    Ok(())
}

运行结果

格式调整后输出如下:

PosMapping(
    {
        "my_first_field": WithPos {
            value: Number(0),
            line: 2,
            column: 19,
        },
        "my_second_field": WithPos {
            value: Mapping(
                {
                    "nested": WithPos {
                        value: String("test"),
                        line: 4,
                        column: 11,
                    }
                }
            ),
            line: 3,
            column: 21,
        },
    }
)

注意事项

  • 行号、列号均从1开始计数
  • 若不需要列号,直接从WithPos结构体中移除对应字段即可
  • 如需支持序列(数组)类型,可参照映射的实现方式自定义PosSequence

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 13:41:40