Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Script Junkie | An XML Document to JavaScript Object Converter (Web Techniques,


<HTML>
<HEAD>
<TITLE>XML to DOM Converter: Listing 3</TITLE>
<APPLET CODE=com.ms.xml.dso.XMLDSO.class WIDTH=100% HEIGHT=30 
             ID=xmldso MAYSCRIPT=true>
<PARAM NAME="url" VALUE="someXMLDocument.xml">
</APPLET>
</HEAD>
<BODY>

<SCRIPT>
_root = xmldso.getDocument().root
results = window.document;


// Simple stack ADT
function stack()
{
     this.depth = 0;
     this.data = new Array();
     this.push = push;
     this.pop = pop;
     this.popTop = popTop;
     this.length = length;
     this.exists = exists;
     return this;
}

// Add an item
function push( item )
{
     this.data[ this.depth ++ ] = item;
}

// Remove last item
function pop()
{
     if ( this.depth )
     {
          this.depth --;
          return this.data[ this.depth ];
     }

     return null;
}

// Remove item at head of stack
function popTop()
{
     if ( this.depth )
     {
          // Pop the top item
         _j = this.data[ 0 ];
         this.depth --;

         // Shuffle the stack up a place
         for ( _i = 0; _i < this.depth; _i ++ )
         {
              this.data[ _i ] = this.data[ _i + 1 ]
          }

              return _j;		
     }
     return null;
}

// Return the size of the stack
function length()
{
     return this.depth;
}

// Does the stack hold the specified item?
function exists( obj )
{
     for ( _i = 0; _i < this.depth; _i ++ )
     {
          if ( this.data[ _i ] == obj )
          {
               return true;
          }
     }

     return false;
}

function xmltodom()
{
     // Cache the XML root, since retrieving it more than once via 	
     // getDocument() causes an error
     var root = _root

     // Holds XML node objects
     var x = new stack();

     // Holds incremental document description tags
     var y = new stack();

     // Holds unique paths of descent to XML leaf nodes 
     var d = new stack();

     x.push( root );
     y.push( "document." + root.tagname + "." );

     while ( x.length() != 0 )
     {
          // Pop top items, so that DOM tree is same way up as XML
          // tree; it would be easier simply to push them in reverse 
          // order, but xmldso.class seems to have problems navigating
               // a node's child-list backwards rather than forwards
               var xmlTag = x.popTop();
                    var docTag = y.popTop();

          // If the XML node is a leaf, assign its DOM equivalent its
          // textual value; otherwise create a new DOM object 
          var docString = docTag.substring( 0, docTag.length - 1 ) + " = ";

          if ( xmlTag.children.length != 0 )
          {
               docString += "new " + xmlTag.tagname + "();<br>";
          }
          else
          {
               docString += "'" + xmlTag.text + "';<br>";
          }

          results.writeln( docString );

          // Check that our XML tag is of type "ELEMENT"
          if ( xmlTag.type == 0 )
          {
               if ( xmlTag.children.length == 0 )
               {
                    // Generate the complete path to the element
                    var iter = xmlTag;
                    var t = new stack();

                    while ( iter.parent != null )
                    {
                         t.push( iter.tagname );
                         iter = iter.parent;
                    }

                    // Reverse the path
                    var path = "";

                    while ( t.length() )
                    {
                         path += t.pop() + ".";
                    }

                    // Check whether this object path has already
                    // been generated
                    if ( !d.exists( path ) )
                    {
                         d.push( path );
                    }
               }
           }

           // Stack children of this XML node
           if ( xmlTag.children )
           {
                // Might seem logical to cache xmlTag.children, but if
                // you do XMLDSO won't work properly
                for ( i = 0; i < xmlTag.children.length; i ++ )
                {
                     x.push( xmlTag.children.item( i ) );

                     // How many instances of a given child node at
                     // this level?
                     var numInstances = 0;

                     for ( j = 0; j < xmlTag.children.length; j ++ )
                     {
                          if ( xmlTag.children.item( i )
                            .tagName == xmlTag.children.item( j ).tagName )
                          {
                               numInstances ++;
                          }
                     }

                     // Get instance index for current node
                     var thisInstance = -1;

                     for ( j = 0; j < i + 1; j ++ )
                     {
                          if ( xmlTag.children.item( j )
                            .tagname == xmlTag.children.item( i ).tagname )
                          {
                               thisInstance ++;
                          }
                     }

                     if ( numInstances > 1 )
                     {
                          y.push( docTag + xmlTag.children.item( i )
                            .tagname + "[ " + thisInstance + " ]." );
                     }
                     else
                     {
                          y.push( docTag + xmlTag.children.item( i )
                            .tagname + "." );
                     }
               }
          }
     }

     // Now generate the ADT function prototypes for this XML document
     results.writeln( "<br>" );

     // Store the tree depth we have parsed
     var objDepth = 0;

     while ( true )
     {
          // Create an array of stacks to hold the collections
          // of sub-elements
          var newTerms = new Array();
          newTerms[ newTerms.length ] = new stack();

          // This will hold the immediate parent of each
          // "newTerms" stack
          var baseTerms = new Array();

          for ( i = 0; i < d.length(); i ++ )
          {
               var str = d.data[ i ];
               var testDepth = 0;
               var lastDot = 0;

               for ( j = 0; j < str.length; j ++ )
               {
                    if ( testDepth == objDepth )
                    {
                         break;
                    }

                    if ( str.charAt( j ) == "." )
                    {
                         testDepth ++;

                         if ( testDepth == objDepth - 1 )
                         {
                              lastDot = j;
                         }
                    }
               }

               if ( j < str.length )
               {
                    var listNum = 0;
                    var baseObject = str.substring( lastDot ? lastDot + 
                                                    1 : 0, j - 1 );

                    // If this parent has already been assigned,
                    // locate it; otherwise assign another one and
                    // add it to the array
                    var fFound = false;

                    for ( k = 0; k < baseTerms.length; k ++ )
                    {
                         if ( baseTerms[ k ] == baseObject )
                         {
                              fFound = true;
                              break;
                         }
                    }

                    if ( !fFound )
                    {
                         listNum = baseTerms.length;
                         newTerms[ listNum ] = new stack();
                         baseTerms[ baseTerms.length ] = baseObject;
                    }
                    else
                    {
                         listNum = k;
                    }

                    str = str.substring( j, str.indexOf( ".", j + 1 ) );

                    // If the sub-element is unique, add it to the
                    // appropriate sub-element collection
                    if ( !newTerms[ listNum ].exists( str ) )
                    {
                         newTerms[ listNum ].push( str );
                    }
               }
          }

          // Parse the two stacks, creating a Javascript object named
          // after each parent, and containing the collection
          // of sub-elements dependent upon that parent
          if ( objDepth != 0 )
          {
               var fItems = false;

               for ( l = 0; l < baseTerms.length; l ++ )
               {
                    if ( baseTerms[ l ].length )
                    {
                         results.writeln( "function " + baseTerms[ l ]
                                          + "()<br>" );
                         results.writeln( "{<br>" );

                         while ( newTerms[ l ].length() )
                         {
                              fItems = true;
                              results.writeln( "    
                                               this." + newTerms[ l ].pop()
                                               + " = new Array();<br>" );
                         }

                         results.writeln( "    
                                          return this;<br>" );
                         results.writeln( "}<br><br>" );
                    }
               }

               // If there were no items, we have finished traversing
               if ( !fItems )
               {
                    break;
               }
          }

          objDepth ++;
     }
}

// Convert the loaded document
xmltodom()
</SCRIPT>

</BODY>
</HTML>





Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.