使用quick-xml反序列化EVTX XML至Rust对象时遇UnexpectedStart错误
问题解决:quick-xml解析EVTX XML时的UnexpectedStart错误
错误原因
报错failed to ds the xml: UnexpectedStart([77, 101, 115, 115, 97, 103, 101])中的字节数组对应字符串Message,核心问题有三点:
- RenderingInfo结构体定义错误:你用
#[serde(rename = "$value")]尝试将<RenderingInfo>下的所有内容解析为message文本,但该标签下除了<Message>还有<Level>、<Task>等多个子元素,解析器遇到这些额外的起始标签时抛出错误。 - 字段命名与serde配置不匹配:结构体字段使用大写开头(如
EventID),不符合Rust蛇形命名规范,且与rename_all = "PascalCase"的自动映射逻辑冲突,会导致字段无法正确匹配XML标签。 - 未处理XML命名空间:
<Event>标签带有xmlns属性,quick-xml默认会严格匹配命名空间,未指定时无法正确识别根元素。
修复方案及修改后代码
修正后的结构体定义
use serde::Deserialize; #[derive(Debug, Deserialize)] #[serde(rename = "Event", namespace = "http://schemas.microsoft.com/win/2004/08/events/event")] #[serde(rename_all = "PascalCase")] pub struct Event { system: System, event_data: EventData, rendering_info: RenderingInfo, } #[derive(Debug, Deserialize)] #[serde(rename_all = "PascalCase")] struct System { // 若需要Provider可取消注释并定义对应结构体 // provider: Provider, event_id: String, version: String, level: String, task: String, opcode: String, keywords: String, time_created: TimeCreated, event_record_id: String, channel: String, computer: String, security: String, // 忽略System下不需要的Correlation、Execution等元素 #[serde(flatten)] _ignored: std::collections::HashMap<String, serde_json::Value>, } #[derive(Debug, Deserialize)] #[serde(rename_all = "PascalCase")] struct TimeCreated { #[serde(rename = "@SystemTime")] system_time: String, } #[derive(Debug, Deserialize)] #[serde(rename_all = "PascalCase")] struct EventData { data: Vec<Data>, } #[derive(Debug, Deserialize)] #[serde(rename_all = "PascalCase")] struct Data { #[serde(rename = "$value")] value: String, #[serde(rename = "@Name")] name: String, } #[derive(Debug, Deserialize)] #[serde(rename_all = "PascalCase")] struct RenderingInfo { #[serde(rename = "@Culture")] culture: String, message: String, // 忽略RenderingInfo下不需要的Level、Task等元素 #[serde(flatten)] _ignored: std::collections::HashMap<String, serde_json::Value>, }
修正后的main.rs代码
use std::path::PathBuf; use evtx::EvtxParser; use quick_xml::de::from_str; fn main() { let fp = PathBuf::from("/Users/arshiamohammadi/Documents/rust/evtx_parser/records/logs1.evtx"); let mut parser = EvtxParser::from_path(fp).unwrap(); for record in parser.records() { match record { Ok(r) => { let event: Event = from_str(&r.data).expect("failed to deserialize the xml"); println!("{:#?}", event); }, Err(e) => eprintln!("{}", e), } break; } }
关键修改说明
- 修复RenderingInfo解析逻辑:移除
$value注解,直接定义message字段匹配XML中的<Message>标签,用#[serde(flatten)]捕获并忽略不需要的子元素,避免解析时遇到未定义标签报错。 - 规范字段命名:将所有结构体字段改为Rust标准的蛇形命名(如
event_id代替EventID),配合rename_all = "PascalCase"自动映射XML的PascalCase标签,既符合规范又保证映射正确。 - 处理XML命名空间:在
Event结构体上添加命名空间注解,明确指定EVTX XML的官方命名空间,让quick-xml能够正确识别根元素。 - 忽略多余元素:在
System和RenderingInfo中添加_ignored字段,用于捕获XML中存在但不需要解析的子元素,避免解析失败。
内容的提问来源于stack exchange,提问作者arshia mohammadi
相关产品推荐
相关产品推荐

