Skip to main content

office_rs/common/
xml_utils.rs

1//! XML处理工具模块
2//! 提供Office文档XML解析和生成的通用功能
3
4use crate::context::ErrorContext;
5use crate::error::{ OfficeError, Result };
6use quick_xml::events::{ BytesEnd, BytesStart, BytesText, Event };
7use quick_xml::{ Reader, Writer };
8use std::collections::HashMap;
9use std::io::{ BufRead, Write };
10
11/// XML命名空间管理器
12#[derive(Debug, Clone)]
13pub struct NamespaceManager {
14    namespaces: HashMap<String, String>,
15    default_namespace: Option<String>,
16}
17
18impl NamespaceManager {
19    /// 创建新的命名空间管理器
20    pub fn new() -> Self {
21        Self {
22            namespaces: HashMap::new(),
23            default_namespace: None,
24        }
25    }
26
27    /// 添加命名空间映射
28    pub fn add_namespace(&mut self, prefix: String, uri: String) {
29        self.namespaces.insert(prefix, uri);
30    }
31
32    /// 设置默认命名空间
33    pub fn set_default_namespace(&mut self, uri: String) {
34        self.default_namespace = Some(uri);
35    }
36
37    /// 获取命名空间URI
38    pub fn get_namespace_uri(&self, prefix: &str) -> Option<&String> {
39        self.namespaces.get(prefix)
40    }
41
42    /// 解析带命名空间的元素名
43    pub fn parse_qualified_name<'a>(&self, name: &'a str) -> (Option<&String>, &'a str) {
44        if let Some(colon_pos) = name.find(':') {
45            let prefix = &name[..colon_pos];
46            let local_name = &name[colon_pos + 1..];
47            (self.get_namespace_uri(prefix), local_name)
48        } else {
49            (self.default_namespace.as_ref(), name)
50        }
51    }
52}
53
54/// XML元素信息
55#[derive(Debug, Clone)]
56pub struct XmlElement {
57    pub name: String,
58    pub attributes: HashMap<String, String>,
59    pub text_content: Option<String>,
60    pub children: Vec<XmlElement>,
61}
62
63impl XmlElement {
64    /// 创建新的XML元素
65    pub fn new<S: AsRef<str>>(name: S) -> Self {
66        Self {
67            name: name.as_ref().to_string(),
68            attributes: HashMap::new(),
69            text_content: None,
70            children: Vec::new(),
71        }
72    }
73
74    /// 添加属性
75    pub fn add_attribute<K: AsRef<str>, V: AsRef<str>>(&mut self, name: K, value: V) {
76        self.attributes.insert(name.as_ref().to_string(), value.as_ref().to_string());
77    }
78
79    /// 获取属性值
80    pub fn get_attribute(&self, name: &str) -> Option<&String> {
81        self.attributes.get(name)
82    }
83
84    /// 设置文本内容
85    pub fn set_text_content<S: AsRef<str>>(&mut self, content: S) {
86        self.text_content = Some(content.as_ref().to_string());
87    }
88
89    /// 添加子元素
90    pub fn add_child(&mut self, child: XmlElement) {
91        self.children.push(child);
92    }
93
94    /// 查找第一个匹配名称的子元素
95    pub fn find_child(&self, name: &str) -> Option<&XmlElement> {
96        self.children.iter().find(|child| child.name == name)
97    }
98
99    /// 查找所有匹配名称的子元素
100    pub fn find_children(&self, name: &str) -> Vec<&XmlElement> {
101        self.children
102            .iter()
103            .filter(|child| child.name == name)
104            .collect()
105    }
106
107    /// 递归查找元素(深度优先搜索)
108    pub fn find_element_recursive(&self, name: &str) -> Option<&XmlElement> {
109        if self.name == name {
110            return Some(self);
111        }
112
113        for child in &self.children {
114            if let Some(found) = child.find_element_recursive(name) {
115                return Some(found);
116            }
117        }
118
119        None
120    }
121}
122
123/// XML解析器
124pub struct XmlParser {
125    namespace_manager: NamespaceManager,
126}
127
128impl XmlParser {
129    /// 创建新的XML解析器
130    pub fn new() -> Self {
131        Self {
132            namespace_manager: NamespaceManager::new(),
133        }
134    }
135
136    /// 添加命名空间
137    pub fn add_namespace(&mut self, prefix: String, uri: String) {
138        self.namespace_manager.add_namespace(prefix, uri);
139    }
140
141    /// 解析XML字符串为元素树
142    pub fn parse_string(&self, xml_content: &str) -> Result<XmlElement> {
143        let mut reader = Reader::from_str(xml_content);
144        reader.config_mut().trim_text(true);
145
146        let context = ErrorContext {
147            operation: Some("解析XML字符串".to_string()),
148            ..Default::default()
149        };
150
151        self.parse_element(&mut reader, &context)
152    }
153
154    /// 解析XML字节流为元素树
155    pub fn parse_bytes(&self, xml_bytes: &[u8]) -> Result<XmlElement> {
156        let mut reader = Reader::from_reader(xml_bytes);
157        reader.config_mut().trim_text(true);
158
159        let context = ErrorContext {
160            operation: Some("解析XML字节流".to_string()),
161            ..Default::default()
162        };
163
164        self.parse_element(&mut reader, &context)
165    }
166
167    /// 内部解析方法
168    fn parse_element<R: BufRead>(
169        &self,
170        reader: &mut Reader<R>,
171        context: &ErrorContext
172    ) -> Result<XmlElement> {
173        let mut buf = Vec::new();
174        let mut element_stack: Vec<XmlElement> = Vec::new();
175        let mut root_element: Option<XmlElement> = None;
176
177        loop {
178            match reader.read_event_into(&mut buf) {
179                Ok(Event::Start(ref e)) => {
180                    let name = String::from_utf8_lossy(e.name().as_ref()).to_string();
181                    let mut element = XmlElement::new(name);
182
183                    // 解析属性
184                    for attr in e.attributes() {
185                        let attr = attr.map_err(|e| {
186                            OfficeError::Xml(quick_xml::Error::InvalidAttr(e)).with_context(
187                                context.clone()
188                            )
189                        })?;
190                        let key = String::from_utf8_lossy(attr.key.as_ref()).to_string();
191                        let value = String::from_utf8_lossy(&attr.value).to_string();
192                        element.add_attribute(key, value);
193                    }
194
195                    element_stack.push(element);
196                }
197                Ok(Event::End(_)) => {
198                    if let Some(element) = element_stack.pop() {
199                        if let Some(parent) = element_stack.last_mut() {
200                            parent.add_child(element);
201                        } else {
202                            root_element = Some(element);
203                            break;
204                        }
205                    }
206                }
207                Ok(Event::Text(ref e)) => {
208                    let text = std::str::from_utf8(e.as_ref()).unwrap_or("");
209                    if let Some(element) = element_stack.last_mut() {
210                        element.set_text_content(text.to_string());
211                    }
212                }
213                Ok(Event::Empty(ref e)) => {
214                    let name = String::from_utf8_lossy(e.name().as_ref()).to_string();
215                    let mut element = XmlElement::new(name);
216
217                    // 解析属性
218                    for attr in e.attributes() {
219                        let attr = attr.map_err(|e| {
220                            OfficeError::Xml(quick_xml::Error::InvalidAttr(e)).with_context(
221                                context.clone()
222                            )
223                        })?;
224                        let key = String::from_utf8_lossy(attr.key.as_ref()).to_string();
225                        let value = String::from_utf8_lossy(&attr.value).to_string();
226                        element.add_attribute(key, value);
227                    }
228
229                    if let Some(parent) = element_stack.last_mut() {
230                        parent.add_child(element);
231                    } else {
232                        root_element = Some(element);
233                        break;
234                    }
235                }
236                Ok(Event::Eof) => {
237                    break;
238                }
239                Err(e) => {
240                    return Err(OfficeError::Xml(e).with_context(context.clone()));
241                }
242                _ => {} // 忽略其他事件
243            }
244            buf.clear();
245        }
246
247        root_element.ok_or_else(|| {
248            OfficeError::parse_error_with_context("root".to_string(), context.clone())
249        })
250    }
251}
252
253/// XML生成器
254pub struct XmlGenerator {
255    namespace_manager: NamespaceManager,
256}
257
258impl XmlGenerator {
259    /// 创建新的XML生成器
260    pub fn new() -> Self {
261        Self {
262            namespace_manager: NamespaceManager::new(),
263        }
264    }
265
266    /// 添加命名空间
267    pub fn add_namespace(&mut self, prefix: String, uri: String) {
268        self.namespace_manager.add_namespace(prefix, uri);
269    }
270
271    /// 将元素树生成为XML字符串
272    pub fn generate_string(&self, element: &XmlElement) -> Result<String> {
273        let mut output = Vec::new();
274        {
275            let mut writer = Writer::new(&mut output);
276            self.write_element(&mut writer, element)?;
277        }
278
279        String::from_utf8(output).map_err(|e| OfficeError::Other(format!("UTF-8编码错误: {}", e)))
280    }
281
282    /// 将元素树写入Writer
283    pub fn write_element<W: Write>(
284        &self,
285        writer: &mut Writer<W>,
286        element: &XmlElement
287    ) -> Result<()> {
288        let context = ErrorContext {
289            operation: Some("生成XML".to_string()),
290            ..Default::default()
291        };
292
293        // 创建开始标签
294        let mut start_tag = BytesStart::new(&element.name);
295
296        // 添加属性
297        for (key, value) in &element.attributes {
298            start_tag.push_attribute((key.as_str(), value.as_str()));
299        }
300
301        if element.children.is_empty() && element.text_content.is_none() {
302            // 空元素
303            writer
304                .write_event(Event::Empty(start_tag))
305                .map_err(|e| OfficeError::Xml(e.into()).with_context(context.clone()))?;
306        } else {
307            // 有内容的元素
308            writer
309                .write_event(Event::Start(start_tag))
310                .map_err(|e| OfficeError::Xml(e.into()).with_context(context.clone()))?;
311
312            // 写入文本内容
313            if let Some(text) = &element.text_content {
314                writer
315                    .write_event(Event::Text(BytesText::new(text)))
316                    .map_err(|e| OfficeError::Xml(e.into()).with_context(context.clone()))?;
317            }
318
319            // 递归写入子元素
320            for child in &element.children {
321                self.write_element(writer, child)?;
322            }
323
324            // 写入结束标签
325            writer
326                .write_event(Event::End(BytesEnd::new(&element.name)))
327                .map_err(|e| OfficeError::Xml(e.into()).with_context(context.clone()))?;
328        }
329
330        Ok(())
331    }
332}
333
334/// XML工具函数
335pub mod utils {
336    use super::*;
337
338    /// 转义XML特殊字符
339    pub fn escape_xml(text: &str) -> String {
340        text.replace('&', "&amp;")
341            .replace('<', "&lt;")
342            .replace('>', "&gt;")
343            .replace('"', "&quot;")
344            .replace('\'', "&apos;")
345    }
346
347    /// 反转义XML特殊字符
348    pub fn unescape_xml(text: &str) -> String {
349        text.replace("&amp;", "&")
350            .replace("&lt;", "<")
351            .replace("&gt;", ">")
352            .replace("&quot;", "\"")
353            .replace("&apos;", "'")
354    }
355
356    /// 验证XML元素名称
357    pub fn is_valid_xml_name(name: &str) -> bool {
358        if name.is_empty() {
359            return false;
360        }
361
362        let first_char = name.chars().next().unwrap();
363        if !first_char.is_alphabetic() && first_char != '_' {
364            return false;
365        }
366
367        name.chars().all(|c| (c.is_alphanumeric() || c == '_' || c == '-' || c == '.'))
368    }
369
370    /// 格式化XML(添加缩进)
371    pub fn format_xml(xml: &str, indent: &str) -> Result<String> {
372        let parser = XmlParser::new();
373        let element = parser.parse_string(xml)?;
374
375        let mut result = String::new();
376        format_element(&element, &mut result, indent, 0);
377        Ok(result)
378    }
379
380    fn format_element(element: &XmlElement, result: &mut String, indent: &str, level: usize) {
381        let current_indent = indent.repeat(level);
382
383        // 开始标签
384        result.push_str(&current_indent);
385        result.push('<');
386        result.push_str(&element.name);
387
388        // 属性
389        for (key, value) in &element.attributes {
390            result.push_str(&format!(" {}=\"{}\"", key, escape_xml(value)));
391        }
392
393        if element.children.is_empty() && element.text_content.is_none() {
394            result.push_str("/>\n");
395        } else {
396            result.push_str(">\n");
397
398            // 文本内容
399            if let Some(text) = &element.text_content {
400                result.push_str(&indent.repeat(level + 1));
401                result.push_str(&escape_xml(text));
402                result.push('\n');
403            }
404
405            // 子元素
406            for child in &element.children {
407                format_element(child, result, indent, level + 1);
408            }
409
410            // 结束标签
411            result.push_str(&current_indent);
412            result.push_str(&format!("</{}>", element.name));
413            result.push('\n');
414        }
415    }
416}
417
418#[cfg(test)]
419mod tests {
420    use super::*;
421
422    #[test]
423    fn test_namespace_manager() {
424        let mut ns_mgr = NamespaceManager::new();
425        ns_mgr.add_namespace(
426            "w".to_string(),
427            "http://schemas.openxmlformats.org/wordprocessingml/2006/main".to_string()
428        );
429
430        let (ns_uri, local_name) = ns_mgr.parse_qualified_name("w:document");
431        assert_eq!(local_name, "document");
432        assert!(ns_uri.is_some());
433    }
434
435    #[test]
436    fn test_xml_parsing() {
437        let xml = r#"<root attr="value"><child>text</child></root>"#;
438        let parser = XmlParser::new();
439        let element = parser.parse_string(xml).unwrap();
440
441        assert_eq!(element.name, "root");
442        assert_eq!(element.get_attribute("attr"), Some(&"value".to_string()));
443        assert_eq!(element.children.len(), 1);
444        assert_eq!(element.children[0].name, "child");
445        assert_eq!(element.children[0].text_content, Some("text".to_string()));
446    }
447
448    #[test]
449    fn test_xml_generation() {
450        let mut element = XmlElement::new("root");
451        element.add_attribute("attr", "value");
452
453        let mut child = XmlElement::new("child");
454        child.set_text_content("text");
455        element.add_child(child);
456
457        let generator = XmlGenerator::new();
458        let xml = generator.generate_string(&element).unwrap();
459
460        assert!(xml.contains("<root attr=\"value\">"));
461        assert!(xml.contains("<child>text</child>"));
462        assert!(xml.contains("</root>"));
463    }
464}