XML Vala Sample

The libxml2 binding is somewhat odd to use from Vala/ Genie, you must do manual memory management via pointers. An abstraction layer/wrapper to the binding would certainly help make things easier. Anyway, here's the sample:

 * Various operations with libxml2: Parsing and creating an XML file


class XmlSample 

        // Line indentation
        indent : int = 0

        def private print_indent (node : string, content : string,  token : char = '+') 
                indent : string = string.nfill (this.indent * 2, ' ')
                stdout.printf ("%s%c%s: %s\n", indent, token, node, content)

        def parse_file (path : string) 
                // Parse the document from path
                doc : Xml.Doc* = Parser.parse_file (path)
                if doc is null
                        stderr.printf ("File %s not found or permissions missing", path)

                // Get the root node. notice the dereferencing operator -> instead of .
                root : Xml.Node* = doc->get_root_element ()
                if root is null
                        // Free the document manually before returning
                        delete doc
                        stderr.printf ("The xml file '%s' is empty", path)

                print_indent ("XML document", path, '-')

                // Print the root node's name
                print_indent ("Root node", root->name)

                // Let's parse those nodes
                parse_node (root)

                // Free the document
                delete doc

        def private parse_node ( node : Xml.Node*) 
                // Loop over the passed node's children
                iter : Xml.Node* = node->children
                while (iter=iter->next) is not null  
                        // Spaces between tags are also nodes, discard them
                        if iter->type is not ElementType.ELEMENT_NODE

                        // Get the node's name
                        node_name : string = iter->name
                        // Get the node's content with <tags> stripped
                        node_content : string = iter->get_content ()
                        print_indent (node_name, node_content)

                        // Now parse the node's properties (attributes) ...
                        parse_properties (iter)

                        // Followed by its children nodes
                        parse_node (iter)
                        // go next
                        iter = iter->next

        def private parse_properties (node : Xml.Node*) 
                // Loop over the passed node's properties (attributes)
                p : Xml.Attr* = node->properties
                while p is not null
                        attr_name : string = p->name

                        // Notice the ->children which points to a Node*
                        // (Attr doesn't feature content)
                        attr_content : string = p->children->content
                        print_indent (attr_name, attr_content, '|')
                        // go next
                        p = p->next

        def static create_simple_xml () : string
                doc : Xml.Doc* = new Xml.Doc ("1.0")

                ns : Xml.Ns* = new Xml.Ns (null, "", "foo")
                ns->type = Xml.ElementType.ELEMENT_NODE
                root : Xml.Node* = new Xml.Node (ns, "simple")
                doc->set_root_element (root)

                root->new_prop ("property", "value")

                subnode : Xml.Node* = root->new_text_child (ns, "subnode", "")
                subnode->new_text_child (ns, "textchild", "another text" )
                subnode->new_prop ("subprop", "escaping \" and  < and >" )

                comment : Xml.Node* = new Xml.Node.comment ("This is a comment")
                root->add_child (comment)

                xmlstr : string
                // This throws a compiler warning, see bug 547364
                doc->dump_memory (out xmlstr)

                delete doc
                return xmlstr


        if args.length < 2
                stderr.printf ("Argument required!\n")

        // Initialisation, not instantiation since the parser is a static class
        Parser.init ()

        simple_xml : string = XmlSample.create_simple_xml ()
        stdout.printf ("Simple XML is:\n%s\n", simple_xml)

        var sample = new XmlSample ()
        // Parse the file listed in the first passed argument
        sample.parse_file (args[1])

        // Do the parser cleanup to free the used memory
        Parser.cleanup ()


Compile and Run

$ valac --pkg libxml-2.0 xmlsample.vala
$ ./xmlsample

        var writer = new Xml.TextWriter.filename ("test.xml")
        writer.set_indent (true)
        writer.set_indent_string ("\t")

        writer.start_document ()
        writer.start_element ("root_element")

        writer.start_attribute ("base64attribute")
        writer.write_base64 ("test", 0, 4)
        writer.end_attribute ()

        writer.write_attribute ("alpha", "abcdef..")

        writer.write_element ("element", "content")
        writer.write_element_ns ("ns", "elementWithNS", "http://www.example.org/test/ns", "contentNS")

        writer.write_comment ("My comment!")
        writer.format_element_ns ("ns", "elementWithFormattedContent", "http://www.example.org/test/ns", "One: %d", 10)

        writer.format_string("%s beer on the wall..", "One")




$ valac --pkg libxml-2.0 xmlwriter.vala
$ ./xmlwriter

The example produces following output in test.xml:

<?xml version="1.0"?>
<root_element base64attribute="dGVzdA==" alpha="abcdef..">
        <ns:elementWithNS xmlns:ns="http://www.example.org/test/ns">contentNS</ns:elementWithNS>
        <!--My comment!-->
        <ns:elementWithFormattedContent xmlns:ns="http://www.example.org/test/ns">One: 10</ns:elementWithFormattedContent>
        <cdataContent><![CDATA[One beer on the wall..]]></cdataContent>

More Genie Examples

Projects/Genie/XmlSample (last edited 2014-03-17 21:56:07 by AlThomas)