easyofd_core/
xml_parse.rs1use quick_xml::Reader;
6use quick_xml::events::Event;
7
8use crate::xml_element::XmlNode;
9
10pub fn parse_xml_to_nodes(xml: &str) -> Result<XmlNode, String> {
16 let mut reader = Reader::from_str(xml);
17 reader.config_mut().trim_text(false);
18 let mut buf = Vec::new();
19 let mut stack: Vec<XmlNode> = Vec::new();
20 let mut root: Option<XmlNode> = None;
21
22 loop {
23 match reader.read_event_into(&mut buf) {
24 Ok(Event::Start(e)) => {
25 let name = local_name(e.name().as_ref());
26 let mut node = XmlNode::element(name);
27 for attr in e.attributes().flatten() {
28 let key = local_name(attr.key.as_ref());
29 let value = attr
30 .normalized_value(quick_xml::XmlVersion::Explicit1_0)
31 .unwrap_or_default()
32 .to_string();
33 node.attrs.push((key, value));
34 }
35 stack.push(node);
36 }
37 Ok(Event::Empty(e)) => {
38 let name = local_name(e.name().as_ref());
39 let mut node = XmlNode::element(name);
40 for attr in e.attributes().flatten() {
41 let key = local_name(attr.key.as_ref());
42 let value = attr
43 .normalized_value(quick_xml::XmlVersion::Explicit1_0)
44 .unwrap_or_default()
45 .to_string();
46 node.attrs.push((key, value));
47 }
48 attach_node(&mut stack, &mut root, node);
49 }
50 Ok(Event::Text(e)) => {
51 let text = e.xml10_content().into_owned();
52 if let Some(top) = stack.last_mut() {
58 match &mut top.text {
59 Some(existing) => existing.push_str(&text),
60 None => top.text = Some(text),
61 }
62 }
63 }
64 Ok(Event::GeneralRef(e)) => {
65 let name = e.xml10_content().into_owned();
66 if let Some(ch) = resolve_xml_entity_ref(&name) {
67 if let Some(top) = stack.last_mut() {
68 match &mut top.text {
69 Some(existing) => existing.push(ch),
70 None => top.text = Some(ch.to_string()),
71 }
72 }
73 }
74 }
75 Ok(Event::End(_)) => {
76 if let Some(node) = stack.pop() {
77 attach_node(&mut stack, &mut root, node);
78 }
79 }
80 Ok(Event::Eof) => break,
81 Err(e) => return Err(format!("XML 解析失败: {e}")),
82 _ => {}
83 }
84 buf.clear();
85 }
86
87 root.ok_or_else(|| "XML 无根元素".to_string())
88}
89
90fn local_name(name: &str) -> String {
92 match name.rsplit_once(':') {
93 Some((_, local)) => local.to_string(),
94 None => name.to_string(),
95 }
96}
97
98fn resolve_xml_entity_ref(name: &str) -> Option<char> {
102 if let Some(entity) = quick_xml::escape::resolve_xml_entity(name) {
103 return entity.chars().next();
104 }
105 let number = if let Some(hex) = name.strip_prefix("#x") {
106 u32::from_str_radix(hex, 16).ok()
107 } else if let Some(decimal) = name.strip_prefix('#') {
108 decimal.parse().ok()
109 } else {
110 None
111 }?;
112 char::from_u32(number)
113}
114
115fn attach_node(stack: &mut [XmlNode], root: &mut Option<XmlNode>, node: XmlNode) {
117 match stack.last_mut() {
118 Some(parent) => parent.children.push(node),
119 None => *root = Some(node),
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[test]
128 fn test_parse_simple() {
129 let xml = r#"<ofd:Page ID="1" BaseLoc="Pages/Page_0/Content.xml"/>"#;
130 let node = parse_xml_to_nodes(xml).unwrap();
131 assert_eq!(node.name, "Page");
132 assert_eq!(node.get_attr("ID"), Some("1"));
133 assert_eq!(node.get_attr("BaseLoc"), Some("Pages/Page_0/Content.xml"));
134 }
135
136 #[test]
137 fn test_parse_nested_with_text() {
138 let xml = r"<Document><CommonData><MaxUnitID>88</MaxUnitID></CommonData></Document>";
139 let node = parse_xml_to_nodes(xml).unwrap();
140 let common = node.child("CommonData").unwrap();
141 let max = common.child("MaxUnitID").unwrap();
142 assert_eq!(max.text.as_deref(), Some("88"));
143 }
144
145 #[test]
146 fn test_parse_multiple_children() {
147 let xml = r#"<Pages><Page ID="1"/><Page ID="2"/></Pages>"#;
148 let node = parse_xml_to_nodes(xml).unwrap();
149 let pages: Vec<_> = node.children_named("Page").collect();
150 assert_eq!(pages.len(), 2);
151 assert_eq!(pages[0].get_attr("ID"), Some("1"));
152 }
153}