use super::{Element, Name, Node};
pub fn serialize(root: &Element) -> Vec<u8> {
let mut out = String::with_capacity(4096);
out.push_str("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
element(&mut out, root);
out.into_bytes()
}
fn element(out: &mut String, e: &Element) {
out.push('<');
name(out, &e.name);
for attr in &e.attrs {
out.push(' ');
name(out, &attr.name);
out.push_str("=\"");
escape_attribute(out, &attr.value);
out.push('"');
}
if e.children.is_empty() && e.self_closing {
out.push_str("/>");
return;
}
out.push('>');
for child in &e.children {
match child {
Node::Element(child) => element(out, child),
Node::Text(t) => escape_text(out, t),
Node::CData(t) => {
out.push_str("<![CDATA[");
out.push_str(t);
out.push_str("]]>");
}
Node::Comment(t) => {
out.push_str("<!--");
out.push_str(t);
out.push_str("-->");
}
Node::ProcessingInstruction(t) => {
out.push_str("<?");
out.push_str(t);
out.push_str("?>");
}
}
}
out.push_str("</");
name(out, &e.name);
out.push('>');
}
fn name(out: &mut String, n: &Name) {
if let Some(prefix) = &n.prefix {
out.push_str(prefix);
out.push(':');
}
out.push_str(&n.local);
}
fn escape_text(out: &mut String, text: &str) {
for c in text.chars() {
match c {
'&' => out.push_str("&"),
'<' => out.push_str("<"),
'>' => out.push_str(">"),
_ => out.push(c),
}
}
}
fn escape_attribute(out: &mut String, value: &str) {
for c in value.chars() {
match c {
'&' => out.push_str("&"),
'<' => out.push_str("<"),
'>' => out.push_str(">"),
'"' => out.push_str("""),
'\t' => out.push_str("	"),
'\n' => out.push_str(" "),
'\r' => out.push_str(" "),
_ => out.push(c),
}
}
}