Visualizing the XML Tree

Using the Notebook Interface and Cell Groups

Many XML tools display an XML document as a collapsible tree, where the nodes correspond to the elements of the document. This example shows how to produce a similar visualization by using cell groupings in a Mathematica notebook.

We will do this by recursively traversing the SymbolicXML expression, and for each XMLElement object, creating a CellGroupData expression, which contains cells for each of that XMLElement object's attributes and children. Each nested CellGroupData will be indented from the previous one. We start with the function to process an XMLElement object.

\(XMLNote[x_XMLElement, \ m_Integer] := 
      Cell[CellGroupData[{
Cell[
              TextData[
StyleBox[x[\([1]\)], 
                  FontFamily -> "Swiss", 
FontWeight -> "Bold", 
                  FontSize -> 15]]], 
Apply[Sequence, 
              Map[XMLNote[#, m] &, x[\([2]\)]]], 
Apply[
              Sequence, 
              Map[XMLNote[#, m + 30] &, x[\([3]\)]]]
}, 
          Open], 
CellMargins -> {{m, 
              Inherited}, {Inherited, Inherited}}]\)

Notice that we use the integer m for indentation. When we map XMLNote onto the XMLElement object's children, we pass a larger value for m, increasing the indentation for the child elements.

A CellGroupData expression contains a list of cells. In the above definition, we've only actually created a cell for the XMLElement x. However, we've mapped XMLNote onto the attribute list. Since this returns a list, we need to apply Sequence to the result in order to merge that list into the CellGroupData expression's list of Cells. We then do the same thing to the children of the XMLElement.

However, we have not yet defined XMLNote to work on attributes. The attributes of an XMLElement object are stored in SymbolicXML as rules. In most cases, the rule contains two strings: the key and the value. However, when namespaces are involved, the first element of the rule may be a list containing two strings: the namespace and the key. We will need to make two definitions to handle the attributes.

\(XMLNote[{an_String, a_String} -> v_String, 
        m_Integer] := 
Cell[
        TextData[
StyleBox[an, 
            FontColor -> Hue[0.6]], " ", 
StyleBox[
            a, FontWeight -> "Bold"], " = ", 
StyleBox[v, 
            Background -> 
              GrayLevel[0.8]]], 
CellMargins -> {{m + 
                5, Inherited}, {Inherited, Inherited}}]\)

\(XMLNote[a_String -> v_String, 
        m_Integer] := 
Cell[
        TextData[
StyleBox[a, 
            FontWeight -> "Bold"], " = ", 
StyleBox[v, 
            Background -> 
              GrayLevel[0.8]]], 
CellMargins -> {{m + 
                5, Inherited}, {Inherited, Inherited}}]\)

We will need one more definition in order to process simple SymbolicXML expressions. The text nodes in an XML document are stored simply as String objects in SymbolicXML. Thus, we need a definition that handles String objects.

XMLNote[s_String, m_Integer] := 
Cell[s, 
        Background -> 
          GrayLevel[
            0.9], 
CellMargins -> {{m + 25, 
              Inherited}, {Inherited, Inherited}}]

With these definitions in place, we can construct a notebook to visualize simple XML documents. Here's a simple example:

d = 
      ImportString[
        ExportString[(1/(Sqrt[2])), "MathML"], "SymbolicXML"]

NotebookPut@
Notebook[{XMLNote[d[[2]], 0]}, 
          CellGrouping -> "Manual", 
          ShowGroupOpenCloseIcon -> "True", 
          ShowCellBracket -> "False"];

Notice that, since the default value of the option "IncludeEmbeddedObjects" is None, we didn't bother to do anything with comments, processing instructions, or anything else that would be stored in an XMLObject. Adding in definitions for these is not difficult and would be a good exercise in processing SymbolicXML.