use anyhow::{anyhow, Result};
use quick_xml::events::Event;
use quick_xml::Reader;
use serde_json::Value;
pub fn parse_xml(content: &str) -> Result<Value> {
let mut reader = Reader::from_str(content);
reader.trim_text(true);
let mut stack: Vec<(String, serde_json::Map<String, Value>)> = Vec::new();
let mut root: Option<(String, serde_json::Map<String, Value>)> = None;
let mut current_text = String::new();
loop {
match reader.read_event() {
Ok(Event::Start(ref e)) => {
let tag_name = String::from_utf8_lossy(e.name().as_ref()).to_string();
let mut element = serde_json::Map::new();
for attr in e.attributes().flatten() {
let key = String::from_utf8_lossy(attr.key.as_ref()).to_string();
let value = String::from_utf8_lossy(&attr.value).to_string();
element.insert(key, Value::String(value));
}
if !current_text.trim().is_empty() && !stack.is_empty() {
let (_, parent) = stack.last_mut().unwrap();
parent.insert(
"text".to_string(),
Value::String(current_text.trim().to_string()),
);
}
current_text.clear();
stack.push((tag_name, element));
}
Ok(Event::Text(e)) => {
let text = e.unescape().unwrap_or_default().to_string();
if !text.trim().is_empty() {
current_text.push_str(&text);
}
}
Ok(Event::CData(e)) => {
let cdata_text = String::from_utf8_lossy(&e).to_string();
current_text.push_str(&cdata_text);
}
Ok(Event::End(ref e)) => {
let tag_name = String::from_utf8_lossy(e.name().as_ref()).to_string();
if let Some((name, mut element)) = stack.pop() {
if name == tag_name {
if !current_text.trim().is_empty() {
if element.is_empty() {
let text_value = Value::String(current_text.trim().to_string());
current_text.clear();
if let Some((_, parent)) = stack.last_mut() {
add_to_parent(parent, &name, text_value);
} else {
root = Some((
name.clone(),
serde_json::Map::from_iter(vec![(name, text_value)]),
));
}
continue;
} else {
element.insert(
"text".to_string(),
Value::String(current_text.trim().to_string()),
);
}
}
current_text.clear();
let element_value = if element.is_empty() {
Value::Object(serde_json::Map::new())
} else if element.len() == 1 && element.contains_key("text") {
element.get("text").unwrap().clone()
} else {
Value::Object(element)
};
if let Some((_, parent)) = stack.last_mut() {
add_to_parent(parent, &name, element_value);
} else {
let mut root_map = serde_json::Map::new();
root_map.insert(name.clone(), element_value);
root = Some((name.clone(), root_map));
}
}
}
}
Ok(Event::Empty(ref e)) => {
let tag_name = String::from_utf8_lossy(e.name().as_ref()).to_string();
let mut element = serde_json::Map::new();
for attr in e.attributes().flatten() {
let key = String::from_utf8_lossy(attr.key.as_ref()).to_string();
let value = String::from_utf8_lossy(&attr.value).to_string();
element.insert(key, Value::String(value));
}
let element_value = Value::Object(element);
if let Some((_, parent)) = stack.last_mut() {
add_to_parent(parent, &tag_name, element_value);
} else {
let mut root_map = serde_json::Map::new();
root_map.insert(tag_name.clone(), element_value);
root = Some((tag_name.clone(), root_map));
}
}
Ok(Event::Eof) => break,
Err(e) => return Err(anyhow!("XML parsing error: {e}")),
_ => {}
}
}
if let Some((_, root_map)) = root {
Ok(Value::Object(root_map))
} else {
Ok(Value::Object(serde_json::Map::new()))
}
}
fn add_to_parent(parent: &mut serde_json::Map<String, Value>, key: &str, value: Value) {
if let Some(existing) = parent.get_mut(key) {
match existing {
Value::Array(arr) => {
arr.push(value);
}
other => {
let _ = std::mem::replace(other, Value::Array(vec![other.clone(), value]));
}
}
} else {
parent.insert(key.to_string(), value);
}
}