eminence / xmltree-rs

Reads an XML file into a simple tree structure
MIT License
39 stars 31 forks source link

More general write methods #20

Open dyst5422 opened 4 years ago

dyst5422 commented 4 years ago

Now that parse_all returns Vec, we need a way to write this back out so that original comment, cdata, etc are included.

eminence commented 4 years ago

Something like:

pub fn write_all<W: Write>(w: W, nodes: &[XMLNode]) -> io::Result<()>;

?

dyst5422 commented 4 years ago

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.

dyst5422 commented 4 years ago

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.

dvtomas commented 4 years ago

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.