/* XmlSample.vala Various operations with libxml2: Parsing and creating a XML file compile & run: valac -o xmlsample --pkg libxml-2.0 XmlSample.vala && ./xmlsample */ using GLib; using Xml; public errordomain XmlError { FILE_NOT_FOUND, XML_DOCUMENT_EMPTY } public class XmlSample : GLib.Object { //line indentation private uint indent = 0; public void print_indent (string node_name, string node_content, char token = '+') { string str_indent = string.nfill(this.indent * 2, ' '); stdout.printf("%s%c%s: %s\n", str_indent, token, node_name, node_content); } public void parse_file (string path) throws XmlError { //parse the document from path Xml.Doc* xml_doc = Parser.parse_file (path); if (xml_doc == null) { throw new XmlError.FILE_NOT_FOUND ("file %s not found or permissions missing", path); } //get the root node. notice the dereferencing operator -> instead of . Xml.Node* root_node = xml_doc->get_root_element (); if (root_node == null) { //free the document manually before throwing because the garbage collector can't work on pointers delete xml_doc; throw new XmlError.XML_DOCUMENT_EMPTY ("the xml'%s' is empty", path); } print_indent ("XML document", path, '-'); //get the root node's name string root_node_name = root_node->name; print_indent ("Root node", root_node_name); //let's parse those nodes parse_node (root_node); //free the document delete xml_doc; } public void parse_node (Xml.Node* node) { this.indent++; //loop over the passed node's children for (Xml.Node* iter = node->children; iter != null; iter = iter->next) { //spaces btw. tags are also nodes, discard them if (iter->type != ElementType.ELEMENT_NODE) continue; string node_name = iter->name; //get the node's name string node_content = iter->get_content (); //get the node's content with <tags> stripped print_indent (node_name, node_content); //now parse the node's properties (attributes) .. parse_properties(iter); //followed by its children nodes parse_node(iter); } this.indent--; } public void parse_properties (Xml.Node* node) { //loop over the passed node's properties (attributes) for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next) { string attr_name = prop->name; //notice the ->children which points to a Node* (Attr doesn't feature content) string attr_content = prop->children->content; print_indent (attr_name, attr_content, '|'); } } public static string create_simple_xml() { Xml.Doc* xmldoc = new Xml.Doc("1.0"); Xml.Ns* ns = new Xml.Ns(); ns->type = Xml.ElementType.ELEMENT_NODE; Xml.Node* root = new Xml.Node(ns,"simple"); xmldoc->set_root_element(root); root->new_prop("property", "value" ); Xml.Node* subnode = root->new_text_child(ns,"subnode",""); subnode->new_text_child(ns, "textchild", "another text" ); subnode->new_prop("subprop", "escaping \" and < and >" ); Xml.Node comment = new Xml.Node.comment( "My comment with strange characters: \" ' > <" ); /* Is this a bug in libxml2? It returns: <!--My comment with strange characters: " ' > <--> instead of <!--My comment with strange characters: " ' > < --> */ root->add_child(comment); string xmlstr; int n; // This throws a compiler warning, see bug 547364 xmldoc->dump_memory(out xmlstr, out n); //delete xmldoc; // Don't delete the xmldoc yourself! return xmlstr; } public static int main (string[] args) { if (args.length != 2) { stderr.printf ("Argument required!\n"); return 1; } // See http://www.vala-project.org/doc/references/libxml-2.0.vapi/ // for API documentation var sample = new XmlSample (); //initialisation, not instantiation since the parser is a static class Parser.init (); string simple_xml = XmlSample.create_simple_xml(); stdout.printf("Simple XML is:\n%s", simple_xml); try { //parse the file listed in the first passed argument sample.parse_file (args[1]); } catch (XmlError err) { //print errors while parsing stderr.printf ("error: %s\n", err.message); } finally { //do the parser cleanup to free the used memory Parser.cleanup (); } return 0; } }