def _nav_to_tree(root):
    """Given an etree containing a navigation document structure
    rooted from the 'nav' element, parse to a tree:
    {'id': <id>|'subcol', 'title': <title>, 'contents': [<tree>, ...]}
    """
    def expath(e, x):
        return e.xpath(x, namespaces=HTML_DOCUMENT_NAMESPACES)
    for li in expath(root, 'xhtml:ol/xhtml:li'):
        is_subtree = bool([e for e in li.getchildren()
                           if e.tag[e.tag.find('}')+1:] == 'ol'])
        if is_subtree:
            # It's a sub-tree and have a 'span' and 'ol'.
            itemid = li.get('cnx-archive-uri', 'subcol')
            shortid = li.get('cnx-archive-shortid')
            yield {'id': itemid,
                   # Title is wrapped in a span, div or some other element...
                   'title': squash_xml_to_text(expath(li, '*')[0],
                                               remove_namespaces=True),
                   'shortId': shortid,
                   'contents': [x for x in _nav_to_tree(li)],
                   }
        else:
            # It's a node and should only have an li.
            a = li.xpath('xhtml:a', namespaces=HTML_DOCUMENT_NAMESPACES)[0]
            yield {'id': a.get('href'),
                   'shortid': li.get('cnx-archive-shortid'),
                   'title': squash_xml_to_text(a, remove_namespaces=True)}