use crate::context::ErrorContext;
use crate::error::{ OfficeError, Result };
use quick_xml::events::{ BytesEnd, BytesStart, BytesText, Event };
use quick_xml::{ Reader, Writer };
use std::collections::HashMap;
use std::io::{ BufRead, Write };
#[derive(Debug, Clone)]
pub struct NamespaceManager {
namespaces: HashMap<String, String>,
default_namespace: Option<String>,
}
impl NamespaceManager {
pub fn new() -> Self {
Self {
namespaces: HashMap::new(),
default_namespace: None,
}
}
pub fn add_namespace(&mut self, prefix: String, uri: String) {
self.namespaces.insert(prefix, uri);
}
pub fn set_default_namespace(&mut self, uri: String) {
self.default_namespace = Some(uri);
}
pub fn get_namespace_uri(&self, prefix: &str) -> Option<&String> {
self.namespaces.get(prefix)
}
pub fn parse_qualified_name<'a>(&self, name: &'a str) -> (Option<&String>, &'a str) {
if let Some(colon_pos) = name.find(':') {
let prefix = &name[..colon_pos];
let local_name = &name[colon_pos + 1..];
(self.get_namespace_uri(prefix), local_name)
} else {
(self.default_namespace.as_ref(), name)
}
}
}
#[derive(Debug, Clone)]
pub struct XmlElement {
pub name: String,
pub attributes: HashMap<String, String>,
pub text_content: Option<String>,
pub children: Vec<XmlElement>,
}
impl XmlElement {
pub fn new<S: AsRef<str>>(name: S) -> Self {
Self {
name: name.as_ref().to_string(),
attributes: HashMap::new(),
text_content: None,
children: Vec::new(),
}
}
pub fn add_attribute<K: AsRef<str>, V: AsRef<str>>(&mut self, name: K, value: V) {
self.attributes.insert(name.as_ref().to_string(), value.as_ref().to_string());
}
pub fn get_attribute(&self, name: &str) -> Option<&String> {
self.attributes.get(name)
}
pub fn set_text_content<S: AsRef<str>>(&mut self, content: S) {
self.text_content = Some(content.as_ref().to_string());
}
pub fn add_child(&mut self, child: XmlElement) {
self.children.push(child);
}
pub fn find_child(&self, name: &str) -> Option<&XmlElement> {
self.children.iter().find(|child| child.name == name)
}
pub fn find_children(&self, name: &str) -> Vec<&XmlElement> {
self.children
.iter()
.filter(|child| child.name == name)
.collect()
}
pub fn find_element_recursive(&self, name: &str) -> Option<&XmlElement> {
if self.name == name {
return Some(self);
}
for child in &self.children {
if let Some(found) = child.find_element_recursive(name) {
return Some(found);
}
}
None
}
}
pub struct XmlParser {
namespace_manager: NamespaceManager,
}
impl XmlParser {
pub fn new() -> Self {
Self {
namespace_manager: NamespaceManager::new(),
}
}
pub fn add_namespace(&mut self, prefix: String, uri: String) {
self.namespace_manager.add_namespace(prefix, uri);
}
pub fn parse_string(&self, xml_content: &str) -> Result<XmlElement> {
let mut reader = Reader::from_str(xml_content);
reader.config_mut().trim_text(true);
let context = ErrorContext {
operation: Some("解析XML字符串".to_string()),
..Default::default()
};
self.parse_element(&mut reader, &context)
}
pub fn parse_bytes(&self, xml_bytes: &[u8]) -> Result<XmlElement> {
let mut reader = Reader::from_reader(xml_bytes);
reader.config_mut().trim_text(true);
let context = ErrorContext {
operation: Some("解析XML字节流".to_string()),
..Default::default()
};
self.parse_element(&mut reader, &context)
}
fn parse_element<R: BufRead>(
&self,
reader: &mut Reader<R>,
context: &ErrorContext
) -> Result<XmlElement> {
let mut buf = Vec::new();
let mut element_stack: Vec<XmlElement> = Vec::new();
let mut root_element: Option<XmlElement> = None;
loop {
match reader.read_event_into(&mut buf) {
Ok(Event::Start(ref e)) => {
let name = String::from_utf8_lossy(e.name().as_ref()).to_string();
let mut element = XmlElement::new(name);
for attr in e.attributes() {
let attr = attr.map_err(|e| {
OfficeError::Xml(quick_xml::Error::InvalidAttr(e)).with_context(
context.clone()
)
})?;
let key = String::from_utf8_lossy(attr.key.as_ref()).to_string();
let value = String::from_utf8_lossy(&attr.value).to_string();
element.add_attribute(key, value);
}
element_stack.push(element);
}
Ok(Event::End(_)) => {
if let Some(element) = element_stack.pop() {
if let Some(parent) = element_stack.last_mut() {
parent.add_child(element);
} else {
root_element = Some(element);
break;
}
}
}
Ok(Event::Text(ref e)) => {
let text = std::str::from_utf8(e.as_ref()).unwrap_or("");
if let Some(element) = element_stack.last_mut() {
element.set_text_content(text.to_string());
}
}
Ok(Event::Empty(ref e)) => {
let name = String::from_utf8_lossy(e.name().as_ref()).to_string();
let mut element = XmlElement::new(name);
for attr in e.attributes() {
let attr = attr.map_err(|e| {
OfficeError::Xml(quick_xml::Error::InvalidAttr(e)).with_context(
context.clone()
)
})?;
let key = String::from_utf8_lossy(attr.key.as_ref()).to_string();
let value = String::from_utf8_lossy(&attr.value).to_string();
element.add_attribute(key, value);
}
if let Some(parent) = element_stack.last_mut() {
parent.add_child(element);
} else {
root_element = Some(element);
break;
}
}
Ok(Event::Eof) => {
break;
}
Err(e) => {
return Err(OfficeError::Xml(e).with_context(context.clone()));
}
_ => {} }
buf.clear();
}
root_element.ok_or_else(|| {
OfficeError::parse_error_with_context("root".to_string(), context.clone())
})
}
}
pub struct XmlGenerator {
namespace_manager: NamespaceManager,
}
impl XmlGenerator {
pub fn new() -> Self {
Self {
namespace_manager: NamespaceManager::new(),
}
}
pub fn add_namespace(&mut self, prefix: String, uri: String) {
self.namespace_manager.add_namespace(prefix, uri);
}
pub fn generate_string(&self, element: &XmlElement) -> Result<String> {
let mut output = Vec::new();
{
let mut writer = Writer::new(&mut output);
self.write_element(&mut writer, element)?;
}
String::from_utf8(output).map_err(|e| OfficeError::Other(format!("UTF-8编码错误: {}", e)))
}
pub fn write_element<W: Write>(
&self,
writer: &mut Writer<W>,
element: &XmlElement
) -> Result<()> {
let context = ErrorContext {
operation: Some("生成XML".to_string()),
..Default::default()
};
let mut start_tag = BytesStart::new(&element.name);
for (key, value) in &element.attributes {
start_tag.push_attribute((key.as_str(), value.as_str()));
}
if element.children.is_empty() && element.text_content.is_none() {
writer
.write_event(Event::Empty(start_tag))
.map_err(|e| OfficeError::Xml(e.into()).with_context(context.clone()))?;
} else {
writer
.write_event(Event::Start(start_tag))
.map_err(|e| OfficeError::Xml(e.into()).with_context(context.clone()))?;
if let Some(text) = &element.text_content {
writer
.write_event(Event::Text(BytesText::new(text)))
.map_err(|e| OfficeError::Xml(e.into()).with_context(context.clone()))?;
}
for child in &element.children {
self.write_element(writer, child)?;
}
writer
.write_event(Event::End(BytesEnd::new(&element.name)))
.map_err(|e| OfficeError::Xml(e.into()).with_context(context.clone()))?;
}
Ok(())
}
}
pub mod utils {
use super::*;
pub fn escape_xml(text: &str) -> String {
text.replace('&', "&")
.replace('<', "<")
.replace('>', ">")
.replace('"', """)
.replace('\'', "'")
}
pub fn unescape_xml(text: &str) -> String {
text.replace("&", "&")
.replace("<", "<")
.replace(">", ">")
.replace(""", "\"")
.replace("'", "'")
}
pub fn is_valid_xml_name(name: &str) -> bool {
if name.is_empty() {
return false;
}
let first_char = name.chars().next().unwrap();
if !first_char.is_alphabetic() && first_char != '_' {
return false;
}
name.chars().all(|c| (c.is_alphanumeric() || c == '_' || c == '-' || c == '.'))
}
pub fn format_xml(xml: &str, indent: &str) -> Result<String> {
let parser = XmlParser::new();
let element = parser.parse_string(xml)?;
let mut result = String::new();
format_element(&element, &mut result, indent, 0);
Ok(result)
}
fn format_element(element: &XmlElement, result: &mut String, indent: &str, level: usize) {
let current_indent = indent.repeat(level);
result.push_str(¤t_indent);
result.push('<');
result.push_str(&element.name);
for (key, value) in &element.attributes {
result.push_str(&format!(" {}=\"{}\"", key, escape_xml(value)));
}
if element.children.is_empty() && element.text_content.is_none() {
result.push_str("/>\n");
} else {
result.push_str(">\n");
if let Some(text) = &element.text_content {
result.push_str(&indent.repeat(level + 1));
result.push_str(&escape_xml(text));
result.push('\n');
}
for child in &element.children {
format_element(child, result, indent, level + 1);
}
result.push_str(¤t_indent);
result.push_str(&format!("</{}>", element.name));
result.push('\n');
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_namespace_manager() {
let mut ns_mgr = NamespaceManager::new();
ns_mgr.add_namespace(
"w".to_string(),
"http://schemas.openxmlformats.org/wordprocessingml/2006/main".to_string()
);
let (ns_uri, local_name) = ns_mgr.parse_qualified_name("w:document");
assert_eq!(local_name, "document");
assert!(ns_uri.is_some());
}
#[test]
fn test_xml_parsing() {
let xml = r#"<root attr="value"><child>text</child></root>"#;
let parser = XmlParser::new();
let element = parser.parse_string(xml).unwrap();
assert_eq!(element.name, "root");
assert_eq!(element.get_attribute("attr"), Some(&"value".to_string()));
assert_eq!(element.children.len(), 1);
assert_eq!(element.children[0].name, "child");
assert_eq!(element.children[0].text_content, Some("text".to_string()));
}
#[test]
fn test_xml_generation() {
let mut element = XmlElement::new("root");
element.add_attribute("attr", "value");
let mut child = XmlElement::new("child");
child.set_text_content("text");
element.add_child(child);
let generator = XmlGenerator::new();
let xml = generator.generate_string(&element).unwrap();
assert!(xml.contains("<root attr=\"value\">"));
assert!(xml.contains("<child>text</child>"));
assert!(xml.contains("</root>"));
}
}