CSV Data Services in Flex
Ted over at PowerSDK recently posted some tips on using Flex’s HTTPService class, and this got me thinking a little about how people perceive data access with Flex.
Most of the Flex documentation talks about using three core data services which include HTTP services, web services and remote objects. It’s certainly possible to have other services though and it’s even possible to roll your own. For example, you might consider the Flash Communication Server a type of service that leverages RTMP. In this post, I’ll show an example of using a comma-separated value (CSV) file as a data service (which will actually leverage the HTTPService class).
Ted notes in his tips that the HTTPService class has a few different ways it can treat the resulting data. The most common, and the default, is to expect XML formatted data as a response and to treat it as an object model that can be easily navigated. In the case of CSV, we instruct the HTTPService class to treat the resulting data as just plain old raw text. From there, it’s up to us to decide what we want to do with it.
<mx:HTTPService id=”svcProducts” url=”catalog.csv” resultFormat=”text” result=”parse( event.result )” />
Note that we still get the result event, and can still refer to the data on the result event object like we might normally for data binding. In this case though, data binding controls to a bunch of raw text isn’t going to be very impressive. In my example I set up a separate property that will be used to hold the parsed CSV data, and then I bind to that property in the rest of the UI.
Now that we’ve requested data, have been notified that we have data, can reference the data, and even setup a place for the parsed data to live, what do we do with the data itself? We parse it!
private var _products:Array = new Array();
public function parse( result:Object ):Void {
var properties:Array = new Array();
var headings:Boolean = false;
var carriage:Number = null;
var comma:Number = null;
var cursor:Number = 0;
var sub:Number = null;
var item:Object = null;
var value:String = null;
var line:String = null;
while( result.indexOf( “\n”, cursor ) != -1 ) {
carriage = result.indexOf( “\n”, cursor );
line = result.substring( cursor, carriage );
cursor = 0;
sub = 0;
item = new Object();
while( line.indexOf( “,”, cursor ) != -1 ) {
comma = line.indexOf( “,”, cursor );
value = line.substring( cursor, comma );
if( !headings ) {
properties.push( value );
} else {
item[properties[sub]] = value;
}
cursor = comma + 1;
sub++;
}
value = line.substring( cursor, line.length - 1 );
if( !headings ) {
properties.push( value );
headings = true;
} else {
item[properties[sub]] = value;
products.push( item );
}
cursor = carriage + 1;
}
}
[ChangeEvent( "productsChanged" )]
public function set products( value:Array ):Void {
_products = value;
}
public function get products():Array {
return _products;
}
I use a fairly brute force method of parsing the text in this example. One of the things I do like is that I use the first row of CSV data to name the properties for the data that is held in each of the following rows. In this fashion we can expose the data in an object-centric fashion. Indeed in the DataGridColumn declarations, you’ll note that I actually specify column names and header text.
<mx:DataGrid width=”100%” height=”100%” dataProvider=”{products}”>
<mx:columns>
<mx:Array>
<mx:DataGridColumn headerText=”ID” columnName=”id” />
<mx:DataGridColumn headerText=”Name” columnName=”name” />
<mx:DataGridColumn headerText=”Price” columnName=”price” />
<mx:DataGridColumn headerText=”Image” columnName=”image” />
<mx:DataGridColumn headerText=”Thumbnail” columnName=”thumbnail” />
</mx:Array>
</mx:columns>
</mx:DataGrid>
You might also have noticed that I use an implicit access method for the products array. Implicit access methods are one of my favorite features of AS2, and in Flex it’s very easy to tie additional event notification to those properties. This means that I can programmatically change the data and any control that is bound to that data will be notified and update itself - which is the case here.
There are many possible improvements to my code that would fit better into the Flex style of development. A couple of which jump right to mind:
A first next step might be to actually subclass HTTPService and make a CSVService class. In this fashion there’d be no need for another property to store the parsed version of the raw data, and binding could also happen in a more direct fashion.
With a CSVService class the second enhancement one might make is a property that states whether or not the CSV data is qualified in nature (i.e. “Field 1″,”Field 2″ vs. Field 1,Field2). Other properties might even be exposed to specify whether or not the header line with the property names is in the data, or if there’s some other delimiter (i.e. a semi-colon instead of a comma) that should be used altogether.
I’ll be the first to admit that you won’t often see CSV data in an enterprise Flex application, but it’s nice to know that you have the option. Knowing how to deal with raw text also provides you with another tool in your bag of tricks. In fact, the motivation for this snippet came from a customer request.
A small sample of this concept in action is available for download.
July 9th, 2005 at 9:59 am
So true, nice post Kevin! I keep finding that HTTPService used with resultFormat=”text” is great for loading htmlText documents into the TextArea component or for loading XML as plain text for later XML parsing. With resultFormat=”XML” or resultFormat=”Object”, the player is forced to parse text recieved into an XML Object when data is received.
HTTPService == Handy!
Cheers,
Ted
August 21st, 2005 at 10:14 am
fyi
Here is the closest thing to a CSV format spec there is:
http://www.ietf.org/internet-drafts/draft-shafranovich-mime-csv-05.txt
mike chambers
mesh@macromedia.com
January 18th, 2006 at 12:44 pm
regrets that does not have registration fees, very interested theme and I would want more oneself to find out, I will read this blog with attention.