Learning Flex 4 | O'Reilly Media

Create an AIR Application to Write Your XML

with 6 comments

I keep finding myself wishing we spent more time in the book discussing Adobe AIR, so I decided to add some extra content here.  Of course, if you run into any questions or concerns, feel free to post them here.

The following application demonstrates a few key uses for AIR’s File() class, particularly:

1) Open a dialog for browsing the file system
2) Create a new file in the file system
3) Use the FileStream() class to write content into a file

Background

The application presented below builds upon various topics covered in Learning Flex 4.  I’m assuming you’ve become comfortable with XML basics. If you need some extra info, chapters 4, 11, 12, and 16 discuss using XML data in Flex applications.  Appendix C (PHP Basics) presents a solution for first concatenating XML as a text string on the server, then receiving that text later in the Flex application as an XML object.  This exercise builds upon these concepts.

The use case for the following application is a scenario where you require fairly dynamic XML data, but you’ve decided it’s impractical to handle XML generation on the web server.  To support the book, I considered the case of the PhotoGallery application (Chapt 12), which relies on some ready-made XML to reference the image content.  While manually typing the XML for about a dozen photos isn’t a big problem, this would quickly become tedious if you were working with a hundred images.  Similarly, the task would consume a lot of time if you frequently recreate or revise the XML.  With this use case in mind, it may be desirable to write a program that automates the XML writing for you.  And hey, if nothing else, writing a 100-line AIR program to handle a programming scenario is generally more fun than writing a couple hundred lines of XML.  :)

Here’s the gist:   When you browse to a folder in your local file system, the AIR app will inspect the files in that directory and build an XML file required for the PhotoGallery application (i.e. photos.xml).  While likely you’d only want to run this utility on a folder of images intended for the PhotoGallery, the app will ensure you’re only adding references to PNG, JPG, and GIF files–this could be helpful in case there was an unassuming LOG file looming about, marooned in the otherwise clean and purposed images folder.

What to Look For

Selecting a directory in the file system:

The function getDirectory()  shows how to use the browseForDirectory() method on a File() object to select and access file objects as directories via the Flex UI.

Creating a New (XML) File and Writing its Content:

The function saveXmlFile() demonstrates how to create a new File() object and use the FileStream() class to write data into the new File(). 

Notice how we use the nativePath property of the selected directory (file.nativePath), followed by the operating system’s separator symbol (File.separator, as a static property), followed by a file name provided by the user, followed by the desired file extension (.xml)–all concatenated together–to establish where to save the new XML file (newXMLFile).

Notice also that the FileStream() object must open() the new file (newXMLFile) in FileMode.WRITE in order to access it for modification purposes.  The FileStream() method writeUTFBytes() is called to pass the concatenated XML string into the new file.  When finished, the close() method is called on the FileStream() to finalize the data input.

The Try..Catch Block!

The Try..Catch block (see the saveXMLFile() function) is good stuff.  It works like this:  First, in the Try section, you try some code that you suspect capable of throwing unanticipated errors; this section should be programmed to handle the task completely, as you’re assuming it will work more often than not.  Next, you follow the Try with a Catch section that allows your program to gracefully catch any “surprise!”, unanticipated errors that might happen when attempting to process the function.  In this case, our Try section ends with an Alert box that notifies us if the XML file is created sucessfully.  Alternatively, our Catch section creates an Alert box notifying the user of the exact error message (er.message) in hopes that it may be detailed enough for the user to consider what caused the error then followup with a successful attempt.

Caveat:  It is too easy to abuse the Try..Catch system as an all-purpose error handling tactic.  Good practice dictates using Try..Catch only when you want to account for unknowns that may throw a wrench, or a chainsaw, or some other mess-making boobalatosomitch into your function.  The Try..Catch system is fairly “expensive” as far as system resources go; plus, it’s just wise to think about your code and handle your use case with a true understanding instead of an attitude that could be epitomized by the phrase “well, I really hope this works, but just in case it doesn’t I’ll Try it and Catch it.”  ….please, just don’t do that.

The String class’s split() method–someString.split(“.”)

The split() method, which is available to String objects, is ultra-useful.  If you have a text String–in this case fileNameAndExt, which is the filename, followed by a dot (.), followed by the file’s extension (.png, .jpg, .gif, .xml, etc..)–you can call the String’s split() method to divide that text string into an Array of String objects as pieces separated by a delimiter.  For this example, the dot (.) is the delimiter. 

This code:
var fileNameArray:Array = new Array();
var fileNameAndExt:String = fileObj.name;      // “photo.jpg”
fileNameArray = fileNameAndExt.split(“.”);

Would create the following index values in the Array:
fileNameArray[0] = “photo”
fileNameArray[1] = “jpg”

Breaking apart the filename into its component portions helps us later when we’re concatenating together our XML text.

The Application Code

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication
 xmlns:fx="http://ns.adobe.com/mxml/2009"
 xmlns:s="library://ns.adobe.com/flex/spark"
 xmlns:mx="library://ns.adobe.com/flex/mx"
 minWidth="600" minHeight="400">

 <fx:Script>
  <![CDATA[
   import mx.controls.Alert;

   private var file:File = new File();
   private var xmlString:String;

   private function getDirectory():void{
    file.browseForDirectory("Select Image Directory");
    file.addEventListener(Event.SELECT, directorySelected);
   }

   private function directorySelected(event:Event):void{
    var filesArray:Array = new Array();
    filesArray = file.getDirectoryListing();

    // create the XML..
    xmlString = '';
    xmlString += '<?xml version="1.0" encoding="utf-8"?>\n';
    xmlString += '<photos>\n';

    // loop through file objects and add XML nodes..
    for each(var fileObj:File in filesArray){
     // make sure the file is not a directory, etc.,
     // or an image format not supported in Flash Player..
     if(((fileObj.isDirectory == false) &&
      (fileObj.isHidden == false) &&
      (fileObj.isPackage == false) &&
      (fileObj.isSymbolicLink == false)) &&
      ((fileObj.extension.toUpperCase() == "PNG") ||
      (fileObj.extension.toUpperCase() == "JPG") ||
      (fileObj.extension.toUpperCase() == "GIF"))){

      // separate "name" into filename AND extension..
      var fileNameAndExt:String = fileObj.name;
      var fileNameArray:Array = new Array();
      var fileName:String;
      var ext:String;
      var path:String;
      var thumbPathNameAndExt:String;
      var imagePathNameAndExt:String;

      fileNameArray = fileNameAndExt.split(".");
      fileName = fileNameArray[0].toString();
      ext = fileNameArray[1].toString();
      // of course, use your own URL here..
      path = "http://www.learningflex4.com/photos/";
      thumbPathNameAndExt = path + fileName + '_th.' + ext;
      imagePathNameAndExt = path + fileNameAndExt;

      xmlString += 
       '<photo credit=""\n' +
        '  title="' + fileName + '"\n' +
        '  thumb="' + thumbPathNameAndExt + '"\n' +
        '  image="' + imagePathNameAndExt + '"/>\n';
     }
    }

    // close the XML and show content in the TextArea..
    xmlString += '</photos>';
    textArea.text = xmlString;
   }

   private function saveXmlFile():void{
    try{
     var newXMLFile:File = new File();
     var fileStream:FileStream = new FileStream();
     var sep:String = File.separator;
     var xmlFilePath:String =
      file.nativePath + sep + fileNameTI.text + ".xml";

     newXMLFile.nativePath = xmlFilePath;
     fileStream.open(newXMLFile, FileMode.WRITE);
     fileStream.writeUTFBytes(xmlString);
     fileStream.close();
     Alert.show("XML Data File Created..", "XML-Builder");
    }

    catch(er:Error){
     trace(er.message);
     Alert.show("XML Data File FAILURE!!" + "\n\n" +
      er.message, "XML-Builder");
    }
   }
  ]]>
 </fx:Script>

 <s:layout>
  <s:VerticalLayout paddingTop="14" horizontalAlign="center"/>
 </s:layout>

 <s:Label text="XML-Builder for the PhotoGallery Application"/>
 <s:Button label="Select Folder to Parse" click="getDirectory()"/>
 <s:TextArea id="textArea" width="90%" height="75%"/>
 <s:HGroup verticalAlign="middle">
  <s:Label text="(provide filename WITHOUT extension)" fontStyle="italic"/>
  <s:TextInput id="fileNameTI"/>
  <s:Button label="Save File" click="saveXmlFile()"/>
 </s:HGroup>

</s:WindowedApplication>
Advertisements

Written by elrobis

7 December 2010 at 8:06 pm

Posted in AIR, Flex 4

6 Responses

Subscribe to comments with RSS.

  1. Thank you very much!

    Mioara

    8 April 2011 at 3:56 am

  2. ???

    Anonymous

    5 July 2012 at 9:17 pm

  3. writing XML is a non issue – it’s APPENDING it in a file that’s the real problem!! And NOBODY is documenting that – but I will once I figure it out:
    :)

    Borg

    17 October 2012 at 5:53 pm

    • Hey @Borg–you can open the file to write in append mode. Off the top of my head, I think it would look like this:

      fileStream.open(newXMLFile, FileMode.APPEND);

      Here’s the official Flex documentation for the FileMode class. It seems this functionality has been available since AIR 1.0, though I’ve never had the occasion to use it, myself:

      http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/filesystem/FileMode.html

      elrobis

      18 October 2012 at 9:17 am

      • Thanks for your reply. Yeah I think I didn’t understand the mechanics of XML in terms of File IO. They are actually two separate events. What you need to do is open the file and read in (consume ) the xml – append it – then WRITE it back to the file. That overwrites the original file contents with your APPENDED XML. If you open the as append then it will add the consumed and appended XML to the end of the existing file and that is not what you would want to do. In my case anyway. But – glad to report that I now have my app working reading, appending and writing XML – awesome!

        Thanks again for your feedback

        Borg

        19 October 2012 at 12:18 pm

  4. ——– file as append ——— (heh I put * file * in brackets so id did not show – duh)

    Borg

    19 October 2012 at 12:21 pm


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: