# DOM-like XML processing

Domlette is 4Suite's lightweight DOM implementation. It is optimized for XPath operations, speed, and relatively low memory overhead. The Domlette API is accessible through Ft.Xml.Domlette. This section describes how to parse, manipulate, and then serialize XML documents using this API.

Below, we briefly summarize the various elements of the API that form the basic life span of Domlette objects.

Parsing XML documents

  The Ft.Xml module contains the functions, `Parse` that gets the job done quickly. See for details. For more advanced parsing, you will need a combination of the reader instances in the Ft.Xml.Domlette module and possibly machinery for constructing `InputSource` objects, which live in the Ft.Xml.InputSource module. gets you started down that path.

Modifying and interacting with XML documents

  The Domlette API for interacting with XML documents—accessible as methods of the various Domlette objects—is similar to the DOM Level 2 specification. See for more information.

Serializing XML documents

  The Ft.Xml.Domlette module provides two functions, `Print` and `PrettyPrint`, for writing your XML documents. The `Print` function writes the XML document precisely as given in the model. On the other hand, the `PrettyPrint` function adds whitespace nodes to your document to try to indent the resulting output nicely. See for details.

## Parsing XML documents

We begin our discussion of the Domlette API by describing how to obtain a model of your XML documents to manipulate further. Because XML documents offer such rich functionality and exist in such varied environments, there can be a surprising amount of work that you must do to simply load your XML documents. We begin by providing a short-cut for easy access. We will then dive into the full suite of document loading utilities.

### Quick access to the Domlette reader API

For basic document manipulations or to get started quickly, the Ft.Xml module offers a quick way to parse XML documents and directly obtain access to the Domlette interface to those documents. Within this module the function of interest is `Parse`.

This function will get you started quickly because it specifically chooses some default values for some of the more advanced parsing features. If you are passing in a string or stream, and the material in applies to your parsing situation, then you will want to use the full-featured API. In brief, if your XML document references external resources, you should not use this convenience function. See instead.

This function returns a Domlette `Document` representing the root of the document from the argument.

`Parse` `source`

  The `Parse` function takes a single argument, which is a string (not Unicode object), file-like object (stream), file path or URI.

    XML = """
    <ham>
    <eggs n='1'/>
    This is the string content with <em>emphasized text</em> text
    </ham>"""

    from Ft.Xml import Parse

    doc = Parse(XML)
    # If the above XML document were located in the file
    # "target.xml", we could have used `Parse("target.xml")`.
    print doc.xpath('string(ham//em[1])')

### The full Domlette reader API

You create Domlette instances by parsing XML documents with the reader system. For general use, the Ft.Xml.Domlette package contains instances of the different reader classes that can be used directly after you import them. These instances include `NonvalidatingReader` and `ValidatingReader`, which provide non-validating parsing and validating parsing services, respectively. The validation in this case refers to DTD validation. For RELAX NG validation, see . All the reader classes (and, hence, their bundled instances) are described in later sections. After you have obtained one of these reader instances, you feed your XML document entity's byte stream to the reader. We summarize the available reader methods below.

`parseUri` `uri`

  The `parseUri` method takes a single argument; this `uri` argument is the absolute URI of the document entity to parse. The URI will be dereferenced by the default resolver.

`parseString` `st` `uri`

  The `parseString` method takes two arguments; `st` is the XML document entity in the form of an encoded Python string (not a Unicode string). See the next section for details on the `uri` argument.

`parseStream` `stream` `uri`

  The `parseStream` method takes two arguments; `stream` is a Python file-like object that can supply the document entity's bytes via `read`() calls. See the next section for details on the `uri` argument.

`parse` `inputSource`

  The `parse` method takes a single argument; `inputSource` is an `Ft.Xml.InputSource.InputSource` object, described in .

The next two sections cover some of the issues that you should understand before using these functions. Then we start seeing some examples in .

### The importance of base URIs

In the first 3 methods listed in the previous section, the `uri` argument is the URI of the document entity that you are feeding to the parser. It is a very important—but often overlooked—concept in document processing.

The URI gives the document entity a unique identifier that can used to refer to the document as a whole. Also, each Domlette node derived from a particular entity inherits that entity's URI as the node's `baseURI` property, unless an alternative base URI was indicated, such as with `xml:base`, or if part of the document was loaded as an external entity or XInclude.

The document's URI is also used as the "base URI" for resolving any relative URI references that may appear within the document itself. Relative URI references may occur in a document in places like:

- <!DOCTYPE> or <!ENTITY>, immediately following the keyword SYSTEM

- `<xsl:import>` and `<xsl:include>`, in the value of the `href` attribute

- `<xi:include>`, in the value of the `href` attribute

- `<exsl:document>`, in the value of the `href` attribute

- the arguments to XSLT's `document()` function

It is a common misconception that relative URI references in a document's content are considered to be relative to the processor's current working directory. They are actually resolved relative to the URI of the document that contains the relative URI reference (more specifically, relative to the URI of the entity in which the reference occurs, keeping in mind that a document may be comprised of multiple entities, i.e., separate files).

In all cases, the document URI that you supply in the reader API must be "absolute", which means that it has a scheme, e.g. "`http://spam/eggs.xml`", not just "`/spam/eggs.xml`" or "`eggs.xml`".

If you know there are not going to be any relative URI references to resolve during initial parsing or during processing of the Domlette by other tools, then you can safely omit the argument, or, preferably, supply a dummy URI like "`urn:dummy`" or "`http://spam/eggs.xml`". If you choose to omit URI arguments from APIs that need them, you may get a Python warning, and a random URI—which is probably not what you want—will be assigned.

If you've understood all this and yet you want to just go ahead and not specify a base URI, you may have to turn off the likely warnings. You can do so with code such as in the following example.

    import  Ft.Xml.Domlette
    import warnings
    def disable_warnings(*args): pass

    warnings.filterwarnings("ignore", category=Warning)
    warnings.showwarning = disable_warnings

    XML = "<spam/>"
    doc  = Ft.Xml.Domlette.NonvalidatingReader.parseString(XML)
    Ft.Xml.Domlette.Print(doc)

You can also in such a case use the convenience function `Ft.Xml.Parse` (see above).

### Parsing XML that's already a Unicode string

Because 4Suite is trying to provide as thin a wrapper as possible to the underlying parser, and due to complexities in the APIs of these parsers, there is no API in 4Suite for parsing Python's Unicode strings.

If your XML is in the form of a Unicode string, you must encode the string as bytes so that the underlying parser can read it. Once you have an encoded string, you can pass it to the reader's `parseString`(), or wrap it in an `InputSource` using the `InputSourceFactory`'s `fromString`(). If the string is not UTF-16 or UTF-8 encoded, then you must tell the reader what encoding it actually uses. You can do this either by writing or replacing the XML declaration in the string itself, or (much easier) setting the optional encoding keyword argument in the reader's `parseString`() method or the `InputSourceFactory`'s `fromString`() method. For an example, see the Akara article on external encoding declarations.

### `NonvalidatingReader`

Use `NonvalidatingReader` for basic parsing. `NonvalidatingReader` performs its parsing without validating against a DTD.

The following example will parse an XML source taken from the supplied URI, which is treated as a URL by the default resolver.

    from Ft.Xml.Domlette import NonvalidatingReader
    doc = NonvalidatingReader.parseUri(
      "http://www.w3.org/2000/08/w3c-synd/home.rss")

The following example also parses an XML source taken from the supplied URI, which is treated as a URL. In this case, the default resolver tries to read the XML source from the filesystem.

    from Ft.Xml.Domlette import NonvalidatingReader
    doc = NonvalidatingReader.parseUri("file:///tmp/spam.xml")

The following example parses XML from the filesystem. When given a relative file path in the local OS's format, we must first convert that path to a URI that our reader objects can use.

    from Ft.Xml.Domlette import NonvalidatingReader
    from Ft.Lib import Uri
    file_uri = Uri.OsPathToUri('spam.xml')
    doc = NonvalidatingReader.parseUri(file_uri)

The following example parses XML from a string. Note that it does not provide a document/base URI.

    from Ft.Xml.Domlette import NonvalidatingReader
    doc = NonvalidatingReader.parseString("<spam>eggs</spam>")

In the following example, we are parsing XML from a string in a case where the document does need a base URI to be specified.

    from Ft.Xml.Domlette import NonvalidatingReader
    s = """<!DOCTYPE spam [ <!ENTITY eggs "eggs.xml"> ]>
    <spam>&eggs;</spam>"""
    doc = NonvalidatingReader.parseString(s, 'http://foo/test/spam.xml')
    # during parsing, the replacement text for &eggs;
    # will be obtained from http://foo/test/eggs.xml

In all of the above examples, doc is now a Domlette node object. 4Suite currently offers one Domlette implementation, written in C, called cDomlette.

### `EntityReader` Examples

Sometimes you need to parse a fragment of XML rather than the full document. If operating in non-validating mode is sufficient, Domlette has a reader that can handle this case. When parsing such a fragment, EntityReader returns a Domlette document fragment rather than a document object.

    from Ft.Xml.Domlette import EntityReader
    s = """
    <spam1>eggs</spam1>
    <spam2>more eggs</spam2>
    """
    docfrag = EntityReader.parseString(s, 'http://foo/test/spam.xml')

The content parsed by `EntityReader` must be an XML External Parsed ENtity. This means that it can't be just any XML document. The main limitation is that it must not have a a document type declaration.

### `ValidatingReader`

If you want to validate a document with a DTD as you parse it, use the `ValidatingReader` object instead. If `ValidatingReader` discovers that the document that it is currently parsing is invalid, then it throws a `Ft.Xml.ReaderException` and does not finish parsing the document. The following example illustrates these concepts.

    # ValidatingReader is a global instance
    from Ft.Xml.Domlette import ValidatingReader

    XML = """<!DOCTYPE a [
      <!ELEMENT a (b, b)>
      <!ELEMENT b EMPTY>
    ]>
    <a><b/><b/></a>"""

    doc = ValidatingReader.parseString(XML, "urn:x-example:valid-a")
    # And of course, as with other readers, you can use `parse`, `parseUri`, and
    # `parseStream` as well.

    # The following document, however, is invalid because an `a` element can only
    # have two `b` children according to its DTD.
    XML = """<!DOCTYPE a [
      <!ELEMENT a (b, b)>
      <!ELEMENT b EMPTY>
    ]>
    <a><b/><b/><b/></a>"""

    # This throws a `Ft.Xml.ReaderException` when it encounters invalid structure,
    # and does not finish parsing the document into `doc`.
    doc = ValidatingReader.parseString(XML, "urn:x-example:invalid-a")

### `NoExtDtdReader`

When using `NonvalidatingReader` to parse a document, that document's DTD is still opened and read to obtain information such as entity declarations and default attribute values. You cannot suppress reading of the internal DTD subset, but you can prevent the external subset from being accessed by using `NoExtDtdReader`. This won't affect the processing of external parameter entities defined in the internal DTD subset. Use this object as you would use `NonvalidatingReader`.

### Creating your own reader instance

In some cases you might not want to use the global reader instances. For instance in multithreaded use, you might want a reader per thread. Or you might want to change some of the parameters on the readers. If so, you can create your own reader instance:

    from Ft.Xml.Domlette import NonvalidatingReaderBase
    reader = NonvalidatingReaderBase()
    doc = reader.parseUri("http://xmlhack.com/read.php?item=1560")

Instead of `NonvalidatingReaderBase`, you could instead use `NoExtDtdReaderBase` or `ValidatingReaderBase`, depending on your needs. Each of these 3 readers take an optional `inputSourceFactory` constructor argument, which you can use to supply a custom URI resolver.

### InputSource objects

All of the previous examples involve parsing URIs or strings of data. You can also handle `InputSource` objects. An `InputSource` is an object that encapsulates a source of encoded text for parsing, and a URI resolver. The advantage to using an `InputSource` is that it provides a standard API to the text stream, and—perhaps more importantly—allows you to associate a custom URI resolver with the stream.

Normally, you can just get an `InputSource` from the factory by calling the appropriate method: `fromUri`(`uri`), `fromString`(`st`), or `fromStream`(`stream`), much like the reader API described earlier. You can then pass the `InputSource` object to the reader's `parse`() method, as in the following example.

    from Ft.Xml import InputSource
    from Ft.Xml.Domlette import NonvalidatingReader
    factory = InputSource.DefaultFactory
    isrc = factory.fromUri("http://xmlhack.com/read.php?item=1560")
    doc1 = NonvalidatingReader.parse(isrc)
    #
    # The factory is reusable. Here we also parse a string:
    #
    isrc = factory.fromString("<spam>eggs</spam>", "http://spam.com/base")
    doc2 = NonvalidatingReader.parse(isrc)
    #
    # InputSource is a file-like object, so you can treat it as such:
    #
    isrc = factory.fromUri("http://xmlhack.com/read.php?item=1560")
    raw_text = isrc.read()
    #
    # The uri/system ID you used for it is maintained
    #
    print isrc.uri
    #
    # You can also create other InputSources from URIs relative to this one
    #
    isrc2 = isrc.resolve("read.php?item=1703")

### Converting from other DOM libraries

You can convert another Python DOM object (e.g. 4DOM or minidom) to a Domlette object using the function `ConvertDocument`:

    from Ft.Xml.Domlette import ConvertDocument
    converted_document = ConvertDocument(oldDocument, documentURI=u'http://www.example.org/')

The `DocumentURI` parameter provides a base URI for the converted nodes. If not specified, attributes documentURI and then baseURI are checked in the source DOM, as defined in DOM Level 3. If no URI is found in this way, a warning is issued and a UUID URI is generated for the new Domlette object.

## Domlette API summary

Interacting with Domlette documents

You will use a large part of the Domlette API to interact with the model of your XML documents. The implementation of this part of the API is found in the Ft.Xml.cDomlette module. This part of the API allows you to navigate around a document and modify the content of that document. It is very similar to the DOM Level 2 specification and follows some of the DOM Level 3 specification; feel free to refer to those specifications and the 4Suite API documentation for details about the intended behavior of this API. You can find brief descriptions of the methods and attributes provided by this API listed below. This API is also nearly the same as the API for xml.dom, which is bundled with Python. The node type constants are inherited directly from `xml.dom.Node`.

Many objects that you will work with in the Domlette API are descendents of the Domlette `Node` class. `Document`s, document fragments (of class `DocumentFragment`), `Element`s, attributes (class `Attr`), text (class `Text`), processing instructions (class `ProcessingInstruction`), and comments (class `Comment`) are all nodes; any node operations are defined on objects of these types, as well. Some operations do not make sense on some objects, however. For example, it does not make sense to add children to an attribute node.

In the DOM model of XML documents, there is a `Document` node which represents the starting point for the other pieces of the document. This node is not the root element of the document; rather, the `Document` node contains the root element as its only element child. The `Document` node may have other children, though, such as processing instructions and comments.

You can easily access properties of a node directly. The following properties are available on any node. These properties generally store information about the structure of the document in the near "vicinity" of the target node.

### Properties available on every `Node` object

attributes

  This is a python dictionary containing the attributes defined on the target node. The key for the dictionary is a tuple containing the namespace and local name of the attribute. The value associated with this attribute name tuple is the attribute (of class `Attr`) itself.

      node = Parse("<foo a='1'/>")
      print node.childNodes[0].attributes

      {(None, u'a'): <Attr at 0x40870ecc: name u'a', value u'1'>}

baseURI

  This is the base URI in scope for the target node as a Python unicode string.

childNodes

  This is the Python list of all the node children of the target node. Note that in DOM terminology, the attributes of a node are not children of that node.

      node = Parse("<foo a='1'/>")
      print node.childNodes

      [<Element at 0x4086052c: name u'foo', 1 attributes, 0 children>]

firstChild

  This is the first child node of the target node. This is equivalent to `childNodes[0]`, and is a useful property for quickly walking the document tree.

      node = Parse("<foo a='1'/>")
      print node.firstChild

      <Element at 0x40860a6c: name u'foo', 1 attributes, 0 children>

lastChild

  This is the last child node of the target node. This is equivalent to `childNodes[-1]`.

      node = Parse("<foo a='1'/><!--Hi!-->")
      print node.lastChild

      <Comment at 0x4087caf4: u'Hi!'>

localName

  This is the local name of the target node as a Python unicode string.

namespaceURI

  This is the namespace URI of the target node as a Python unicode string.

nextSibling

  This is the node immediately following the target node, or `None` if the target node is the last child of its parent (or if the target node is an attribute, as attributes are unordered).

nodeValue

  This is the value of the target node as a Python unicode string, if the target node has a string value. If not, this is `None`. To illustrate some of the possibilities, attributes and text nodes have values, while elements and documents do not.

ownerDocument

  This is the `Document` node in which the target node is contained.

parentNode

  This is the parent of the target node. If the target node is a `Document` node, then this will be `None`; `Document` nodes do not have parents.

prefix

  This is the namespace prefix of the current node, or `None` if the current node does not (or cannot) have a namespace prefix.

previousSibling

  This is the node immediately preceding the target node, or `None` if the target node is the first child of its parent (or if the target node is an attribute, as attributes are unordered).

rootNode

  This is a synonym for ownerDocument.

xmlBase

  This is a synonym for baseURI.

In addition to accessing the structure relative to a node, there are also a set of operations that we can perform on these structures, including a variety of operations for modifying the document. Some of these methods allow you to add new nodes in various places; note that in the DOM, only Document nodes can create new nodes. See for details. The following methods are available on any node.

### Methods available to every `Node` object

`appendChild` `node`

  This method adds `node` as the last child of the current instance. This is useful for manually building a document in breadth-first document order.

`insertBefore` `newChild` `refChild`

  This method adds the node `newChild` to the current instance immediately before child node `refChild`.

`replaceChild` `newChild` `oldChild`

  This method replaces the child node `oldChild` with the `newChild` node.

`removeChild` `oldChild`

  This method removes the `oldChild` node as a child of the instance node.

`cloneNode` `deep`

  This method returns a new copy of the current instance. If (and only if) `deep` is true, then we copy deeply: the node's attributes and children are also copied deeply.

`isSameNode` `otherNode`

  This method determines whether the instance node and `otherNode` are the same node based upon object identity.

`normalize`

  This method merges any adjacent text nodes in the attributes or descendents of the current instance.

`hasChildNodes`

  This method returns true if and only if the instance node has any child nodes.

`xpath` `expr` `explicitNss`

  This method evaluates the XPath expression `expr` with the current instance as the expression context and returns an appropriately-valued result. The `explicitNss` parameter is optional; it is a Python dictionary mapping namespace prefixes to namespaces for use in the expression. See for details.

In addition to their behavior as nodes, `Document` nodes are uniquely responsible for a number of tasks. For example, only `Document` nodes can create other nodes. The following methods are availble only to `Document` nodes.

### Methods available to `Document` objects

`createElementNS` `namespaceURI` `qualifiedName`

  This method creates and returns a new `Element` with the given namespace URI and qualified name.

`createAttributeNS` `namespaceURI` `qualifiedName`

  This method creates and returns a new attribute (`Attr` object) with the given namespace URI and qualified name.

`createTextNode` `data`

  This method creates and returns a new `Text` node with the string value of `data`.

`createProcessingInstruction` `target` `data`

  This method creates and returns a new processing instruction (`ProcessingInstruction` object) with the given `target` name and contents taken from `data`.

`createComment` `data`

  This method creates and returns a new `Comment` with the string value of `data`.

`createDocumentFragment`

  This method creates and returns a new, empty document fragment (`DocumentFragment` object).

`importNode` `importedNode` `deep`

  Nodes can only belong to one document at a time. This method creates a copy of the node `importedNode` that belongs to the instance (but which does not yet have a parent). If (and only if) `deep` is true, then we copy deeply: the node's attributes and children are also copied deeply and imported.

Document nodes also have a number of properties that are not found on other nodes. These properties are summarized in the following list.

### Properties available on `Document` objects

doctype

  This is the document type of the document.

documentElement

  This is the root element of the document.

documentURI

  This is the URI that identifies the document.

implementation

  This is the `DOMImplementation` that created the document.

publicId

  This is the public ID of the doctype of this document.

rootNode

  This refers to the current instance.

systemId

  This is the system ID of the doctype of this document.

unparsedEntities

  This is the list of unparsed entities in the current document.

Attributes (`Attr` objects) do not have any special methods, but they do have a few additional properties. These properties are summarized in the following list.

### Properties available on `Attr` objects

name

  This is the qualified name of the current instance.

nodeName

  This is a synonym for the name property.

ownerElement

  This is a synonym for the parentNode property.

specified

  You will probably never need this property. It is always `1`. DOM says it should be `0` if it is present through defaulting, rather than explicitly specified in the document. This is only possible if the DOM implementation preserves certain details from DTD processing, which 4Suite never does. Therefore the value is always `0`.

value

  This is a synonym for the nodeValue property.

Since attributes can only be attached to elements, `Element` objects have a set of special methods for managing which attributes are attached to them. We describe these methods below.

### Methods available to `Element` objects

`hasAttributeNS` `namespaceURI` `localName`

  This method returns true if the current instance has an attribute with the given namespace URI and local name, and false otherwise.

`getAttributeNS` `namespaceURI` `localName`

  This method returns the attribute value of the attribute with the given namespace URI and local name, if one exists. If not, this returns `None`.

`getAttributeNodeNS` `namespaceURI` `localName`

  This method returns the `Attr` object of the attribute with the given namespace URI and local name, if one exists. If not, this returns `None`.

`removeAttributeNS` `namespaceURI` `localName`

  This method removes the attribute with the given namespace URI and local name from the current instance element.

`removeAttributeNode` `node`

  This method removes the attribute `node` from the current instance element.

`setAttributeNS` `namespaceURI` `qualifiedName` `value`

  This method adds an attribute or replaces an attribute with the specified namespace URI and qualified name and sets the content of that attribute to `value`.

`setAttributeNodeNS` `node`

  This method adds or replaces an attribute using the `Attr` object `node`.

`Element`s also have several properties above and beyond what they get from being `Node`s. See the list below for details.

### Properties available on `Element` objects

nodeName

  This is the qualified name of the current instance.

tagName

  This is a synonym for nodeName.

Both `Text` and `Comment` nodes are also more general `CharacterData` nodes in the DOM. `CharacterData` nodes have several additional properties and methods for managing the string data that they contain. The individual `Text` and `Comment` nodes, however, do not add any functionality to their general `CharacterData` parent class. You can find descriptions of the properties and methods offered by `CharacterData` objects below.

### Properties available on `CharacterData` objects

data

  This is the string content of the current instance.

length

  This is the length of the string content of the current instance.

nodeValue

  This is a synonym for data.

### Methods available to `CharacterData` objects

`insertData` `offset` `data`

  This method inserts the string `data` into the content of the current instance at the index specified by `offset`.

`appendData` `data`

  This method appends the string `data` to the end of the value of the current instance.

`replaceData` `offset` `count` `data`

  This method replaces `count` number of characters found at index `offset` in the current instance with the string `data`.

`substringData` `offset` `count`

  This method retrieves and returns the part of the string value of the current instance that begins at index `offset` and extends `count` characters.

`deleteData` `offset` `count`

  This method deletes the part of the string value of the current instance that begins at index `offset` and extends `count` characters.

A few DOM actions are not "owned" by any individual document. In effect, they are general-purpose operations. They can be found in `DOMImplementation` objects. One such precreated instance can be conveniently found at and used from Ft.Xml.Domlette.implementation. The general methods that such a `DOMImplementation` object offers are listed below.

### `DOMImplementation` methods:

`createDocument` `namespaceURI` `qualifiedName` `doctype`

  This method creates and returns a `Document` object of the specified type with its document element.

`createRootNode` `documentURI`

  This method creates a `Document` object with the specified `documentURI`.

`hasFeature` `feature` `version`

  This method tests whether the DOM implementation implements a specific feature.

### What about `getElementsByTagName`()?

The `getElementsByTagName`() method isn't supported, because there are better options. In particular, you can just use XPath:

    doc.xpath(u"//tagname")

For more possibilities, see `getElementsByTagName` Alternatives.

## Serializing Domlette nodes

Domlette comes with a couple of very fast printer functions which also go to great pains to correctly handle character encoding issues: `Print` and `PrettyPrint`. Here are some serialization examples using the Domlette printers, given a node '`node`' (it doesn't have to be a document node).

    from Ft.Xml.Domlette import Print, PrettyPrint

    # basic serialization to sys.stdout
    Print(node)

    # ... with extra whitespace (indenting)
    PrettyPrint(node)

    # ... using a single tab, rather than 2 spaces, to indent at each level
    PrettyPrint(node, indent='\t')

    # serializing to a utf-8 encoded file
    f = open('output.xml','w')
    Print(node, stream=f)
    f.close()

    # ... to an iso-8859-1 encoded file
    f = open('output.xml','w')
    Print(node, stream=f, encoding='iso-8859-1')
    f.close()

    # ... to an ascii encoded string
    import cStringIO
    buf = cStringIO.StringIO()
    Print(node, stream=buf, encoding='us-ascii')
    buf.close()
    s = buf.getvalue()

    # Normally, output syntax (XML or HTML) is chosen based on the DOM type,
    # which is automatically detected. A Domlette or XML DOM can be output in
    # HTML syntax if the asHtml=1 argument is given.
    PrettyPrint(node, asHtml=1)

See also: Serializing XML from DOM or Domlette documents

## Building a DOM from scratch

As an alternative to parsing a preexisting XML document, you can also build a document model from the ground up. The Ft.Xml.Domlette module contains a `DOMImplementation` instance named implementation which provides a set of methods for initializing new `Document`s. The `implementation.createRootNode` method provides a natural approach for creating an XPath model root node. This is similar to the DOM idea of a document node and even closer to a DOM document fragment (multiple element children are allowed). The `implementation.createDocument` method, on the other hand, is designed to come close to the DOM interface.

    doc = implementation.createRootNode('file:///article.xml')

is the equivalent of

    from Ft.Xml import EMPTY_NAMESPACE
    doc = implementation.createDocument(EMPTY_NAMESPACE, None, None)
    doc.baseURI = 'file:///article.xml'

And similarly

    from Ft.Xml import EMPTY_NAMESPACE
    doc = implementation.createRootNode('file:///article.xml')
    docelement = doc.createElementNS(EMPTY_NAMESPACE, 'article')
    doc.appendChild(docelement)

is the equivalent of

    from Ft.Xml import EMPTY_NAMESPACE
    doc = implementation.createDocument(EMPTY_NAMESPACE, 'article', None)
    doc.baseURI = 'file:///article.xml'

If you want as much fidelity to the DOM API as Domlette offers, use `implementation.createDocument`. If you just want to create a document or other such root-level node, and never mind the strange parameters, use `implementation.createRootNode`.

## XPath query

You can easily perform XPath queries by use the `xpath` method for cDomlette nodes as follows:

    from Ft.Xml.Domlette import NonvalidatingReader
    doc = NonvalidatingReader.parseString("<spam>eggs<a/><a/></spam>")
    print doc.xpath(u'//a')
    print doc.xpath(u'string(/spam)')

Notice: this is nothing like W3C DOM's XPath query module. The emphasis, as usual with Domlette, is on speed, simplicity and pythonic-ness.

The API, in brief:

`node.xpath(expr[, explicitNss])`

- node - will be used as core of the context for evaluating the XPath

- expr - XPath expression in string or compiled form

- explicitNss - (optional) any additional or overriding namespace mappings in the form of a dictionary that maps prefixes to namespace URIs. The base namespace mappings are taken from in-scope declarations on the given node. This explicit dictionary is superimposed on the base mappings.

For additional details, see .

## More on base URIs

For some users, always specifying a base URI feels like an inconvenience. Perhaps they always generate XML sources from text or streams without naturally associated URIs, and they have to figure out schemes to come up with base URIs for the parse. But there is good reason for this pickiness. Just ask one of the users who got bitten by carelessness with base URIs in practice. It's better to always put some amount of thought into base URIs when processing XML, and 4Suite encourages this.

Note that 4Suite only enforces the requirement for base URIs in cases where they are needed to make sense of a requested operation. Your document must have a valid base URI if you use external entities, XInclude, xsl:import, xsl:include, the XSLT document() function, the EXSLT exsl:document element, or any other operations that require access to an external resource. If your main use for URI resolution is XSLT import and includes, you can avoid having to give valid base URIs by using XSLT include paths.

A valid base URI starts with a scheme, such as `http:`. A simple name, such as "spam" is a valid relative URI reference, but not a valid base URI. Without a base URI, a relative reference is no more useful than an apartment number given without the address of the entire apartment building. Merging a base URI with a relative reference is a string operation that is undertaken in a standard manner, and is generally only useful when the base URI is hierarchical; that is, it is a URL using one of the common schemes that have slashes as path separators (e.g., http:, ftp:, gopher:, and most file: URLs). The built-in 4Suite URI resolver `Ft.Lib.Uri.BASIC_RESOLVER` knows how to perform such resolution.

## Why does Domlette diverge from the DOM specification?

Domlette is not fully DOM compliant, but it does provide an interface very close to DOM Level 2. One of the design principles for Domlette is that where DOM and XPath disagree, XPath wins.