Building a tree-view for xml data

Even though I am a long time Emacs user I only recently realized that I am really missing treeviews. There are numerous applications like getting an overview of a directory hierarchy  or structure of documents in xml format. It’s a nice thing to have and of course Emacs does not stand back and has something to offer:

To name a couple that immediatly show up. For the dyi people emacs brings an xml parser and a thing called „Tree-widget“ that allows for building of tree-views.  I gave these a try. And since the web does not seem to overflow with information about using tree widget I dump it here – hoping that other beginners might profit from it:

 

 
(setq xml "<post time=\"20050716234509\" id=\"010101\"><login><id>123</id></login><msg>A message.</msg><info>Information!</info></post>")
(setq xml-2 "<post><login><id>123</id></login><msg>Here is the message</msg><info>Information!</info></post>")
(setq xml-3 "<id>123</id>")
 
(setq root (with-temp-buffer
             (insert xml)
             (xml-parse-region (point-min) (point-max))))
 
(setq root-2 (with-temp-buffer
               (insert xml-2)
               (xml-parse-region (point-min) (point-max))))
 
(setq root-3 (with-temp-buffer
               (insert xml-3)
               (xml-parse-region (point-min) (point-max))))
 
(setq root-4 (with-current-buffer "some-more-complex.xml"
               (xml-parse-region (point-min) (point-max))))
 
(xml-node-children (car root-3))
 
(cdr (car (car (nthcdr 1 (car root)))))
 
(let* (
       (post (car root))
       (attrs (xml-node-attributes post)))
  attrs)
 
(require 'tree-widget)
 
(defun xml-to-tree-widget (xml)
  (interactive)
  (cond 
   ((stringp xml)
    (widget-convert 'item :tag xml))
   ((listp xml)
    (let ((attributes (xml-node-attributes xml))
          (attrib-widgets nil)
          (children (xml-node-children xml))
          (current-node))
      (progn
        (when attributes
          (setq attrib-widgets (mapcar (lambda (prop)
                                         (widget-convert 'item
                                                         :tag (format "%s=%s" (symbol-to-string (car prop)) (cdr prop))))
                                       attributes)))
        (setq current-node (widget-convert 'tree-widget 
                                           :tag (symbol-to-string (car xml))
                                           :args (append (if children 
                                                             (mapcar (lambda (node)
                                                                       (xml-to-tree-widget node))
                                                                     children)
                                                           nil)
                                                         attrib-widgets)))
        current-node ) ) ) ) )
 
(xml-to-tree-widget (car root))
 
(xml-to-tree-widget (car root-3))
 
(xml-to-tree-widget "text")
 
(defun test-tree-widget ()
  (interactive)
  (with-current-buffer (get-buffer-create "*tree-widget-test*")
    (erase-buffer)
    (setq-local my-tree-widget (widget-create (xml-to-tree-widget (car root-4))))
    (switch-to-buffer (current-buffer))))
 
;; This one will show the tree-widget in an empty buffer.
(test-tree-widget)

It’s not yet a piece of code that can be (use-package)ed or even (require)d 🙂 I entered the code in emacs and then „C-x C-e“ where necessary. When the initial xml data contains spaces (e.g. between a closing and the next opening tag) the resulting „tree“ contains empty lines, too.

3 Kommentare

  1. Hello Mr. Matthias,

    Interesting post, just want to mention a few things:

    1. The *setq* with *root-4* might need *get-buffer-create* as well if the buffer does not exist.
    2. I don’t have *symbol-to-string*, I replaced it with *symbol-name* instead.

    It wouldn’t be hard to make a JSON or YAML widget with this idea as well.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.