如何获取Serde与serde-yaml解析字段时的行/字符位置?
用Serde + serde-yaml获取YAML字段的行号与字符位置
要实现解析YAML时捕获每个字段的原始行号(及可选字符位置),核心是利用serde-yaml提供的位置访问能力,通过自定义反序列化包装类型实现。
实现步骤
- 定义带位置的包装结构体
创建泛型结构体存储字段值与对应位置信息:
use serde::de::{self, Deserialize, Deserializer}; use serde_yaml::Location; #[derive(Debug)] struct WithPos<T> { pub value: T, pub line: usize, pub column: usize, // 可选字符位置,不需要可移除 }
- 为包装体实现自定义反序列化
在反序列化流程中捕获当前位置,同时解析字段实际值:
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) }
- 解析示例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
相关产品推荐
相关产品推荐

