Moving Around Through XML


Remember that Flash uses a DOM-based system for working with XML. Under this system, after you have loaded and parsed some XML, you end up with a tree structure . The most common way to manipulate the XML is to walk through the XML tree to see what's there and extract the relevant information.

Each "node" in an XML tree is actually an object. This is important! These objects can be either an XML element or text. For example, the structure listed here has five nodes. Three of those nodes are XML elements (<person>, <firstName>, and <lastName>). The remaining two nodes (John and Doe) are text nodes. When you're stepping though a tree structure to extract information from the nodes, how are you going to know whether you're in an XML element or a text node? Actually, it's easy. The XML object has a property called nodeType. This read-only property can have a value of 1 or 3. A value of 1 tells you that you are inside an XML element (between the brackets). A value of 3 tells you that you're inside a text node.

 <person>      <firstName>          John      </firstName>      <lastName>          Doe      </lastName>  </person> 

Is nodeType the only XML property that you have access to? Of course not. This would be a good time to stop and take a look at some of the XML object properties that you have access to when you are working with nodes:

  • nodeType. This read-only property can have a value of 1 or 3. A value of 1 tells you that you are inside an XML element (between the brackets). A value of 3 tells you that you're inside a text node.

  • nodeName. If you are inside of an XML element node, you can use this property to retrieve the element's name .

  • nodeValue. If you're interested in retrieving the text inside a text node, you look for the NodeValue.

Remember these properties. They'll come in handy in a moment.

Now picture the previous XML structure graphically represented as a tree. You would see something like Figure 23.1. You might have noticed that your tree structure looks just like a family tree. In fact, Flash uses a lot of genealogy terminology, such as parent, child, and sibling, for moving around inside XML trees.

Figure 23.1. The XML hierarchy as a tree structure.

graphics/23fig01.gif

After Flash has loaded and parsed an XML document, the XML object that it was loaded into contains a single link to the new data structure, firstChild. This property of the XML object points to the root node of the tree. In the previous case, it would point to the <person> node. To find the firstChild for any XML object you have created, you just have to reference this firstChild. To test this, work your way through Exercise 23.2.

Exercise 23.2 Determining the Node in the Tree Structure

In this exercise, you'll use the file that you created in Exercise 23.1 and add some code to determine the nodeName and nodeType of the root node.

  1. You should still be working in the file that you created in the previous exercise, or you can open plantList1.fla from the Chapter_23/Assets folder on the CD.

    Right now, your code, which is attached to frame 1 of the main timeline, looks like this:

     // create your new XML object  plantXML = new XML();  // create your onLoad function  plantXML.onLoad = function(valid) {     if (valid) {         trace("Got good XML.");      }else{         trace("That's just not right.");      }  }  // Load your XML  plantXML.load("plantListStructured.xml"); 
  2. This time, if the XML loads correctly, you want to determine the nodeType and nodeName of the root node. Delete the first trace statement inside the if statement, and add the code between the asterisks :

     // create your new XML object  plantXML = new XML();  // create your onLoad function  plantXML.onLoad = function(valid) {     if (valid) { //**************************************************************          rootNode = this.firstChild;          trace("rootNode.nodeType + " : " + rootNode.nodeName);  //**************************************************************      }else{         trace("That's just not right.");      }  }  // Load your XML  plantXML.load("plantListStructured.xml"); 
  3. Save and test your file. This time, your output should look like Figure 23.2.

    Figure 23.2. You can figure out what the nodeValue and nodeName of the root node are by checking the properties of this.firstChild.

    graphics/23fig02.gif

All nodes in the tree have properties to point to other nodes. The way they all work is illustrated in Figure 23.3. (The current node is the highlighted one.)

Figure 23.3. The nodes in an XML document all have relationships to each other based on their position in the document tree.

graphics/23fig03.gif

The childNodes property might seem a bit confusing, but it's actually pretty simple; it's just an array of all the child nodes under a given node. In the Figure 23.2 example, childNodes would contain two elements, node 3 and node 4.

When you are working with XML, you already know that you're dealing with nodes. I've alluded to child nodes. You can also have parent nodes and sibling nodes. It's really not as bad as it sounds. Go back and look at the XML structure that we were using earlier:

 <person>      <firstName>          John      </firstName>      <lastName>          Doe      </lastName>  </person> 

You have all three types of nodes represented. <firstName> is the child node of <person>. <lastName> is also a child node of <person>. <person> is the parent node for both <firstName> and <lastName>. <firstName> and <lastName> are siblings.

It goes even further than that. Remember that text also counts as a node, so John is the child of <firstName> and Doe is the child of <lastName>. John and Doe, however, are not siblings. You can use some additional XML properties to access these different nodes:

  • parentNode. This is a read-only property that will reference the parent of any given node. If the node has no parent, this property returns null.

  • firstChild. This is another read-only property and references the first child for a given parent node. As with the parentNode property, if there is no firstChild, this property returns null.

  • lastChild. Similar to firstChild, this property references the lastChild, if it exists, in a parent list. If a parent has no children, this property returns null.

  • nextSibling. This property references the next sibling in a parent's child list. As with the other properties, this one returns null if there is no next sibling. It is read-only.

  • previousSibling. This property references the previous sibling in a parent's child list. As with the other properties, this one returns null if there is no previous sibling. It is read-only.

Now it's time to pull all this together and do something useful with all these new properties.

Using Node Properties

A complex but often used method of exploiting these properties is to print the XML document to the output window and format it for debugging purposes. This is particularly useful when you are importing XML from an outside source.

Think for a moment about how you are going to accomplish this. After your XML document loads, you'll simply have to make a call to a function that will read and print the data. In addition, you will want to print a nicely formatted XML tree to the output windowwith brackets around XML element nodes, indents, the whole works.

Sounds pretty straightforward doesn't it? It's not. To build the tree, you need to actually do something called a "depth first search." This means that you go down as deep into the tree as you can and then move on. You can do this by having the function call itself, or behave recursively.

Recursion is very useful when you are dealing with tree structures. It can also be a difficult topic to wrap your head around. The first issue that you need to deal with before you plunge into the next exercise is how recursion works.

Recursion is just a way of subdividing a problem into increasingly smaller pieces. It is possible to use recursion when writing a function. A recursive function internally calls itself. It sends itself a smaller and smaller piece to solve until it has worked down to the smallest piece. Then it climbs back out to solve the next bigger piece. If you think about it, that works well with XML because every piece of XML looks the same (for example, every node has child nodes).

In the next exercise, you'll use the nodeType, nodeValue, firstChild, and nextSibling properties, along with recursion to read the XML tree structure and format it for printing to the output window. You'll continue to build on the file that you've already started.

Exercise 23.3 Using the XML Properties

  1. You should still be working in the file that you created in the first exercise, or you can open plantList2.fla from the Chapter_23/Assets folder on the CD. Remember to save it to the same directory on your hard drive where the XML file exists. This time, you're going to call a function that will print the contents of your XML file, neatly formatted, in the output window.

  2. Inside the if statement, delete these two lines:

     rootNode = this.firstChild;           trace("rootNode.nodeType + " : " + rootNode.nodeName); 
  3. In your now empty if statement, make your call to the printXML() function.

     _root.printXML(this.firstChild); 

    Time to start building your function. This function will need two pieces of information. It's going to need to know the current node and the value of the variable spaces . When you set up the call to the function, you passed in the first node this.firstChild. You didn't assign any value to spaces. That means that, on the first pass through the function, the variable spaces will have no value.

  4. Start by setting up the shell for the function. Add some space to the top of your Actions list and enter the following code:

     function printXML(node, spaces){ } 

    If you are going to print out a tree structure, you need to know whether you are in an XML element or text node. Fortunately, that's easy to test for using the nodeType property. You know when you start building your tree that the first node is going to be an XML elementit has to be because you start your tree with the root node. Inside the function, you're going to set up a conditional statement that checks the node type and prints the appropriate information in the output window.

    First you'll look at the completed code, and then you'll add it to your file.

    The completed function looks like this:

     function printXML(node, spaces){     if (node.nodeType == 3){         trace(spaces + node.nodeValue);      }else{         trace(spaces +"<" + node.nodeName + ">");          var tempNode = node.firstChild;          while (tempNode != null){             printXML(tempNode, spaces+"    ");              tempNode = tempNode.nextSibling;          }          trace(spaces +"</" + node.nodeName + ">");      }  } 

    The first thing that you do is check the nodeType. If it's a text node, you print the value of whatever is in the spaces variable along with the nodeValue.

    If it's not a text node, it must be an XML element, so you need to print the opening and closing angle brackets along with the nodeName. If it's an XML element, you need to check whether it has any children. Here's where things get interesting. If the XML element does have a child, you enter the while loop, which immediately calls the function again. This is where the recursion sneaks in. The first call to the function is dead in the water until the second call to the function successfully completes. It probably is easiest to understand this if you look at the actual XML document that you are working against.

     The XML document that you'll be loading looks like this:  <plantList>      <perennial>          <variety>Rose</variety>          <commonName>Zepherine Drouhin</commonName>          <bloomColor>Deep Pink</bloomColor>          <light>Part Shade</light>      </perennial>      <perennial>          <variety>Delphineum</variety>          <commonName>Delphineum Independence</commonName>          <bloomColor>Deep Blue</bloomColor>          <light>Sun</light>      </perennial>  </plantList> 

    On the very first call to the function, you pass in the value of this.firstChild. This will be the value for the node. Spaces have no value. this.firstChild happens to be <plantList>. Because <plantList> is not a text node, you immediately drop into the else statement. The trace statement prints this:

     <plantList> 

    Then it checks to see whether <plantList> has any children. As it happens, <plantList> does have a child, so you drop into the while loop and call the function again. This time you pass in the value of the firstChild of <plantList> (tempNode), along with the variable spaces, which you add some blank spaces to. Go back to the top of your function, and check to see if you're working with a text node. You're not, so you're back into the else statement. Now the printout will look like this:

     <plantList>      <perennial> 

    Just like before, you check to see if <perennial> has a child; it does, so you generate a new call to your function.

    You're still working with an element node, so you drop into the else statement and print this:

     <plantList>      <perennial>          <variety> 

    <variety> has a child node, so you call your function again. This time you're working with a text nodeno else statement for you on this pass. Because your node type is 3, you print your spaces along with the value of the node. You skip the else statement entirely on this pass and drop to the next line of code, which prints your spaces and closing tag. Now your printout looks like this:

     <plantList>        <perennial>          <variety>              Rose          </variety> 

    Notice what happened here. You finally completed one complete function call. That means that the previous call can now complete processing.

    For the previous call to the function, the call with which you determined that <variety> was a child of <perennial> can now continue at this line:

     tempNode = tempNode.nextSibling; 

    You check to see whether <variety> has a siblingit does. The sibling is <commonName>. Because the nextSibling gets passed to tempNode, tempNode is not null and you go back to the top of the while loopand call your function again!

    You continue processing until there are no more siblings. When you have no more siblings, you'll drop out of the loop and close your <perennial> tag. The whole process gets repeated again for the next <perennial> tag.

  5. So now that you've walked through the code, go ahead and complete your function. Your final code should look like this:

     function printXML(node, spaces){     if (node.nodeType == 3){         trace(spaces + node.nodeValue);      }else{         trace(spaces +"<" + node.nodeName + ">");          var tempNode = node.firstChild;          while (tempNode != null){             printXML(tempNode, spaces+"   ");              tempNode = tempNode.nextSibling;          }          trace(spaces +"</" + node.nodeName + ">");      }  }  myXML = new XML();  myXML.onLoad = function(valid){     if (valid){         _root.printXML(this.firstChild);      }  }  myXML.load("plantListStructured.xml") 
  6. Save and test your file. The printout in the Output window will look like this:

     <plantList>     <perennial>        <variety>           Rose        </variety>        <commonName>           Zepherine Drouhin        </commonName>        <bloomColor>           Deep Pink        </bloomColor>        <light>           Part Shade        </light>     </perennial>     <perennial>        <variety>           Delphinium        </variety>        <commonName>           Delphinium Independence        </commonName>        <bloomColor>           Deep Blue        </bloomColor>        <light>           Sun        </light>     </perennial>  </plantList> 

What's the deal with all those blank lines? This brings up another issue that you need to deal with when working with Flash and XML: whitespace.

Whitespace, XML, and Flash

Unlike with most other XML parsers, whitespace is significant to Flash. Flash sees spaces, tabs, hard returns, and so on as actual nodes in the tree. This means that you should always look for the actual contents of a node rather than just assuming that the data that you want will be at a certain position in the tree. If you rerun this code but change the XML file being loaded to plantList.xml, which has all whitespace stripped out, you'll get a much nicer format:

 <plantList>     <perennial>        <variety>           Rose        </variety>        <commonName>           Zepherine Drouhin        </commonName>        <bloomColor>           Deep Pink        </bloomColor>        <light>           Part Shade        </light>     </perennial>     <perennial>        <variety>           Delphineum        </variety>        <commonName>           Delphineum Independence        </commonName>        <bloomColor>           Deep Blue        </bloomColor>        <light>           Sun        </light>     </perennial>  </plantList> 

If you are running Internet Explorer 5.0 or higher, you can use IE's built-in parser to display your XML file in the browser window. (See Figure 23.4.) IE will build a color -coded collapsible tree for youand it doesn't care about whitespace.

Figure 23.4. If you have Internet Explorer 5+, you can display a color-coded version of your XML file directly in the browser window.

graphics/23fig04.gif

But you still have to deal with whitespace in Flash. The most recent version of the Flash Player (R41/42) has an ignoreWhite property that also eliminates the whitespace problem, but be careful using this: If your target audience doesn't have the most recent revision of the Player, they're going to have problems with your parsed data.

XML and Attributes

In addition to the element and text nodes you've looked at in XML files, element nodes can also have attributes . In fact, depending on your data, you can use attributes to replace child nodes altogether. Why would you do that? Because it's fast. Remember that one of the things that slows parsing of XML files in Flash is a deep file structure. If you will have only one type of a particular piece of information for a node, you can make that information an attribute of the node.

If you look at the original XML for the example that you've been working with (see Listing 23.2) you'll see that the first XML element node had an attribute called name . You'll notice that the attribute didn't get picked up when you walked recursively through your file. That's just because you didn't ask Flash to look for any attributes. What if you want to know what the attributesas well as the nodesare? Well, it turns out that attributes are actually pretty easy to deal with!

Listing 23.2 The plantList Element Node Has an Attribute Called name With a Value of My List
 <plantList name="My List">      <perennial>          <variety>Rose</variety>          <commonName>Zepherine Drouhin</commonName>          <bloomColor>Deep Pink</bloomColor>          <light>Part Shade</light>      </perennial>      <perennial>          <variety>Delphineum</variety>          <commonName>Delphineum Independence</commonName>          <bloomColor>Deep Blue</bloomColor>          <light>Sun</light>      </perennial>  </plantList> 

Each XML element node has an attributes property. This property is actually an object, with each property of the object being an attribute. For example, this code would loop over an XML element node and print all the attributes and their values:

 for (i in node.attributes){     trace(i+" = " + node.attributes[i]));  } 

Note

This is a slightly different version of the for loop than you've seen. Instead of setting up a counter to loop through your code until a condition is met, the for/in loop lets you loop through the properties of an objectin this case, the array of properties associated with the attributes object.


If you want the printXML() function that you created in the last exercise to pick up attributes as well as child nodes, all you need to do is add the code between the asterisks in Listing 23.3.

Listing 23.3 To Get the printXML() Function to Also Pick Up an Attribute of Any Tags,You Just Have to Add a Little Bit More Code and Change One Line
 function printXML(node, spaces){     if (node.nodeType == 3){         trace(spaces + node.nodeValue);      }else{ // *****************************************************************          var attr = "";          for (var i in node.attributes){             attr += " " + i + "='" + node.attributes[i]+"'";          }          trace(spaces +"<" + node.nodeName + attr + ">");  // *****************************************************************          var tempNode = node.firstChild;          while (tempNode != null){             printXML(tempNode, spaces+" ");              tempNode = tempNode.nextSibling;          }          trace(spaces +"</" + node.nodeName + ">");      }  }  myXML.load("plantListAttributes.xml") 

Note that the line

 trace(spaces +"<" + node.nodeName + ">"); 

was replaced by this line:

 trace(spaces +"<" + node.nodeName + attr + ">"); 

But what if you want to convert the existing XML file to use attributes rather than child nodes? You know that this would be faster, and it would work nicely with the existing data. You just need to alter the existing code (see Listing 23.2) to this:

 <plantList name="My List">      <perennial variety="Rose" commonName="Zepherine Drouhin"      bloomColor="Deep Pink" light="Part Shade"></perennial>      <perennial variety="Delphineum" commonName="Delphineum Independence"      bloomColor="Deep Blue" light="Sun"></perennial>      </plantList> 

You can still use the exact same code that you were using in the earlier exercise to walk through this file and print it in the output window. If you want to print the attributes as well, you'll need to add the code in Listing 23.3. You can test this by saving plantListAttributes.xml and plantListAttributes (from the Chapter_23/Assets folder on the CD) to the same directory on your hard drive and then testing the Flash file.

So now you know how to navigate through your XML structure. What else can you do with XML?



Inside Flash
Inside Flash MX (2nd Edition) (Inside (New Riders))
ISBN: 0735712549
EAN: 2147483647
Year: 2005
Pages: 257
Authors: Jody Keating

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net