docs: 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)}