如何使用Rust的quick_xml读取XML文件并实时打印读取事件?
用quick_xml实现统一格式的事件生成器
先定义统一的事件结构,把quick_xml的原生事件转换成你需要的(事件类型, 标签, 属性, 文本)格式:
use quick_xml::Reader; use quick_xml::events::{Event, BytesStart, BytesEnd}; use std::io::BufRead; // 定义事件类型枚举 #[derive(Debug)] enum EventType { Start, End, Text, Eof, } // 统一格式的事件结构体 #[derive(Debug)] struct ParsedEvent { event_type: EventType, tag: Option<String>, attributes: Vec<String>, text: Option<String>, }
接下来实现生成器函数,读取XML事件并完成格式转换,同时处理文本分段的问题(XML文本可能被拆成多个Text事件):
fn xml_event_generator<R: BufRead>(mut reader: Reader<R>) -> impl Iterator<Item = ParsedEvent> { let mut buf = Vec::new(); let mut accumulated_text = String::new(); std::iter::from_fn(move || { buf.clear(); match reader.read_event_into(&mut buf) { Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e), Ok(Event::Eof) => { // 输出剩余的累积文本 let text_event = if !accumulated_text.is_empty() { let text = accumulated_text.clone(); accumulated_text.clear(); Some(ParsedEvent { event_type: EventType::Text, tag: None, attributes: vec![], text: Some(text), }) } else { None }; // 最后返回Eof事件 text_event.or_else(|| Some(ParsedEvent { event_type: EventType::Eof, tag: None, attributes: vec![], text: None, })) } Ok(Event::Start(e)) => { // 先输出之前累积的文本 let text_event = if !accumulated_text.is_empty() { let text = accumulated_text.clone(); accumulated_text.clear(); Some(ParsedEvent { event_type: EventType::Text, tag: None, attributes: vec![], text: Some(text), }) } else { None }; text_event.or_else(|| { // 转换Start事件格式 let tag = e.name().local_name().to_string_lossy().into_owned(); let attributes = e.attributes() .filter_map(|a| a.ok()) .map(|a| a.value.to_string_lossy().into_owned()) .collect(); Some(ParsedEvent { event_type: EventType::Start, tag: Some(tag), attributes, text: None, }) }) } Ok(Event::Text(e)) => { // 累积文本(处理转义) if let Ok(unescaped) = e.unescape() { accumulated_text.push_str(&unescaped.into_owned()); } // 继续读取下一个事件,暂不返回文本 xml_event_generator(reader).next() } Ok(Event::End(e)) => { // 先输出累积的文本 let text_event = if !accumulated_text.is_empty() { let text = accumulated_text.clone(); accumulated_text.clear(); Some(ParsedEvent { event_type: EventType::Text, tag: None, attributes: vec![], text: Some(text), }) } else { None }; text_event.or_else(|| { // 转换End事件格式 let tag = e.name().local_name().to_string_lossy().into_owned(); Some(ParsedEvent { event_type: EventType::End, tag: Some(tag), attributes: vec![], text: None, }) }) } // 忽略注释、CData等其他事件 _ => xml_event_generator(reader).next(), } }) }
用法示例
fn main() { let xml = r#"<root attr="test"><item>Hello <bold>World</bold></item></root>"#; let reader = Reader::from_str(xml); let mut generator = xml_event_generator(reader); while let Some(event) = generator.next() { match event.event_type { EventType::Start => println!("(start, {:?}, {:?}, None)", event.tag.unwrap(), event.attributes), EventType::End => println!("(end, {:?}, [], None)", event.tag.unwrap()), EventType::Text => println!("(text, None, [], {:?})", event.text.unwrap()), EventType::Eof => break, } } }
关键细节
- 文本累积:解决XML文本被拆分为多个
Text事件的问题,确保输出完整的元素文本 - 格式统一:所有事件都通过
ParsedEvent结构体标准化,方便后续处理 - 错误处理:示例中直接panic,实际项目可改为返回
Result类型,符合Rust错误处理规范
后续PyO3集成提示
可以用PyO3把ParsedEvent转换成Python字典或自定义类,再通过PyO3的生成器API(比如结合PyIter)暴露给Python使用,就能实现Rust解析XML,Python消费事件的流程。
内容的提问来源于stack exchange,提问作者safetyduck
相关产品推荐
相关产品推荐

