Open dyst5422 opened 4 years ago
Something like:
pub fn write_all<W: Write>(w: W, nodes: &[XMLNode]) -> io::Result<()>;
?
Yeah. I have an external prototype I've been using that separates out the event emission from the write, so that one could write multiple XMLNodes/Elements to the same write stream as separate operations.
Note that the emit methods should be internalized with self
.
pub fn write_tree<W: std::io::Write>(
xml_tree: Vec<XMLNode>,
w: &mut W,
) -> Result<(), xml::writer::Error> {
write_tree_with_config(xml_tree, w, EmitterConfig::new())
}
pub fn write_tree_with_config<W: std::io::Write>(
xml_tree: Vec<XMLNode>,
w: &mut W,
config: xml::writer::EmitterConfig,
) -> Result<(), xml::writer::Error> {
use xml::common::XmlVersion;
use xml::writer::events::XmlEvent;
use xml::writer::EventWriter;
let mut emitter = EventWriter::new_with_config(w, config);
emitter.write(XmlEvent::StartDocument {
version: XmlVersion::Version10,
encoding: None,
standalone: None,
})?;
for node in &xml_tree {
xmlnode_emit(node, &mut emitter)?;
}
Ok(())
}
pub fn xmlnode_emit<B: std::io::Write>(node: &XMLNode, emitter: &mut xml::writer::EventWriter<B>) -> Result<(), xml::writer::Error> {
use xml::writer::events::XmlEvent;
match node {
XMLNode::Element(elem) => xmlelement_emit(elem, emitter)?,
XMLNode::Text(text) => emitter.write(XmlEvent::Characters(text))?,
XMLNode::Comment(comment) => emitter.write(XmlEvent::Comment(comment))?,
XMLNode::CData(cdata) => emitter.write(XmlEvent::CData(cdata))?,
XMLNode::ProcessingInstruction(name, data) => match data.to_owned() {
Some(string) => emitter.write(XmlEvent::ProcessingInstruction {
name,
data: Some(&string),
})?,
None => emitter.write(XmlEvent::ProcessingInstruction { name, data: None })?,
},
}
Ok(())
}
pub fn xmlelement_emit<B: std::io::Write>(element: &xmltree::Element, emitter: &mut xml::writer::EventWriter<B>) -> Result<(), xml::writer::Error> {
use xml::attribute::Attribute;
use xml::name::Name;
use xml::namespace::Namespace;
use std::borrow::Cow;
use xml::writer::events::XmlEvent;
let mut name = Name::local(&element.name);
if let Some(ref ns) = element.namespace {
name.namespace = Some(ns);
}
if let Some(ref p) = element.prefix {
name.prefix = Some(p);
}
let mut attributes = Vec::with_capacity(element.attributes.len());
for (k, v) in &element.attributes {
attributes.push(Attribute {
name: Name::local(k),
value: v,
});
}
let empty_ns = Namespace::empty();
let namespace = if let Some(ref ns) = element.namespaces {
Cow::Borrowed(ns)
} else {
Cow::Borrowed(&empty_ns)
};
emitter.write(XmlEvent::StartElement {
name,
attributes: Cow::Owned(attributes),
namespace,
})?;
for node in &element.children {
xmlnode_emit(node, emitter)?;
}
emitter.write(XmlEvent::EndElement { name: Some(name) })?;
Ok(())
}
Edit:
Just realized I should have taken &[XMLNode]
as inputs rather than vectors.
Also, I'm completely open to changes in external API shape. I'm really jumping around in the best api myself - whether to have methods that consume XMLNode to write, or having a trait implemented on Vec\<XMLNode>, etc etc.
I would also appreciate making Element::_write
public, or having some other way to be able to build up the XML incrementally from individual Elements over a longer course of time, without being forced to wait until all of them are available.
Now that parse_all returns Vec, we need a way to write this back out so that original comment, cdata, etc are included.