Processing XML in flex3 - apache-flex

First time here asking a question and still learning on how to format things better... so sorry about the format as it does not look too well.
I have started learning flex and picked up a book and tried to follow the examples in it. However, I got stuck with a problem. I have a jsp page which returns xml which basically have a list of products. I am trying to parse this xml, in other words go through products, and create Objects for each product node and store them in an ArrayCollection. The problem I believe I am having is I am not using the right way of navigating through xml.
The xml that is being returned from the server looks like this:
<?xml version="1.0" encoding="ISO-8859-1"?><result type="success">
<products>
<product>
<id>6</id>
<cat>electronics</cat>
<name>Plasma Television</name>
<desc>65 inch screen with 1080p</desc>
<price>$3000.0</price>
</product>
<product>
<id>7</id>
<cat>electronics</cat>
<name>Surround Sound Stereo</name>
<desc>7.1 surround sound receiver with wireless speakers</desc>
<price>$1000.0</price>
</product>
<product>
<id>8</id>
<cat>appliances</cat>
<name>Refrigerator</name>
<desc>Bottom drawer freezer with water and ice on the door</desc>
<price>$1200.0</price>
</product>
<product>
<id>9</id>
<cat>appliances</cat>
<name>Dishwasher</name>
<desc>Large capacity with water saver setting</desc>
<price>$500.0</price>
</product>
<product>
<id>10</id>
<cat>furniture</cat>
<name>Leather Sectional</name>
<desc>Plush leather with room for 6 people</desc>
<price>$1500.0</price>
</product>
</products></result>
And I have flex code that tries to iterate over products like following:
private function productListHandler(e:JavaFlexStoreEvent):void
{
productData = new ArrayCollection();
trace(JavaServiceHandler(e.currentTarget).response);
for each (var item:XML in JavaServiceHandler(e.currentTarget).response..product )
{
productData.addItem( {
id:item.id,
item:item.name,
price:item.price,
description:item.desc
});
}
}
with trace, I can see the xml being returned from the server. However, I cannot get inside the loop as if the xml was empty. In other words, JavaServiceHandler(e.currentTarget).response..product must be returning nothing. Can someone please help/point out what I could be doing wrong.
My JavaServiceHandler class looks like this:
package com.wiley.jfib.store.data
{
import com.wiley.jfib.store.events.JavaFlexStoreEvent;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.net.URLLoader;
import flash.net.URLRequest;
public class JavaServiceHandler extends EventDispatcher
{
public var serviceURL:String = "";
public var response:XML;
public function JavaServiceHandler()
{
}
public function callServer():void
{
if(serviceURL == "")
{
throw new Error("serviceURL is a required parameter");
return;
}
var loader:URLLoader = new URLLoader();
loader.addEventListener(Event.COMPLETE, handleResponse);
loader.load(new URLRequest(serviceURL));
// var httpService:HTTPService = new HTTPService();
// httpService.url = serviceURL;
// httpService.resultFormat = "e4x";
// httpService.addEventListener(Event.COMPLETE, handleResponse);
// httpService.send();
}
private function handleResponse(e:Event):void
{
var loader:URLLoader = URLLoader(e.currentTarget);
response = XML(loader.data);
dispatchEvent(new JavaFlexStoreEvent(JavaFlexStoreEvent.DATA_LOADED) );
// var httpService:HTTPService = HTTPService(e.currentTarget);
// response = httpService.lastResult.product;
// dispatchEvent(new JavaFlexStoreEvent(JavaFlexStoreEvent.DATA_LOADED) );
}
}
}
Even though I refer to this as mine and it is not in reality. This is from a Flex book as a code sample which does not work, go figure.
Any help is appreciated.
Thanks
john

I just tried following code:
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
creationComplete="onComplete();">
<mx:Script>
<![CDATA[
import radekg.JavaServiceHandler;
private function onComplete():void {
var jsh:JavaServiceHandler = new JavaServiceHandler();
for each ( var node:XML in jsh.response.products.product ) {
trace( node.id.text() );
trace( node.cat.text() );
trace( node.name.text() );
trace( node.desc.text() );
trace( node.price.text() );
trace("---------------------------------------");
}
}
]]>
</mx:Script>
</mx:WindowedApplication>
And radekg/JavaServiceHandler.as which emulates your handler class:
package radekg
{
public class JavaServiceHandler
{
public var response:XML = <result type="success">
<products>
<product>
<id>6</id>
<cat>electronics</cat>
<name>Plasma Television</name>
<desc>65 inch screen with 1080p</desc>
<price>$3000.0</price>
</product>
<product>
<id>7</id>
<cat>electronics</cat>
<name>Surround Sound Stereo</name>
<desc>7.1 surround sound receiver with wireless speakers</desc>
<price>$1000.0</price>
</product>
<product>
<id>8</id>
<cat>appliances</cat>
<name>Refrigerator</name>
<desc>Bottom drawer freezer with water and ice on the door</desc>
<price>$1200.0</price>
</product>
<product>
<id>9</id>
<cat>appliances</cat>
<name>Dishwasher</name>
<desc>Large capacity with water saver setting</desc>
<price>$500.0</price>
</product>
<product>
<id>10</id>
<cat>furniture</cat>
<name>Leather Sectional</name>
<desc>Plush leather with room for 6 people</desc>
<price>$1500.0</price>
</product>
</products></result>;
}
}
And as a result I'm getting:
6
electronics
Plasma Television
65 inch screen with 1080p
$3000.0
---------------------------------------
7
electronics
Surround Sound Stereo
7.1 surround sound receiver with wireless speakers
$1000.0
---------------------------------------
8
appliances
Refrigerator
Bottom drawer freezer with water and ice on the door
$1200.0
---------------------------------------
9
appliances
Dishwasher
Large capacity with water saver setting
$500.0
---------------------------------------
10
furniture
Leather Sectional
Plush leather with room for 6 people
$1500.0
---------------------------------------
Your JavaServiceHandler.result points to the XML root tag so in other words, replace your:
for each (var item:XML in JavaServiceHandler(e.currentTarget).response.products..product )
with:
for each (var item:XML in JavaServiceHandler(e.currentTarget).response.products.product )
Hope that helps.

I'm, not exactly an XML / Flex whiz, but is this a typo?
for each (var item:XML in JavaServiceHandler(e.currentTarget).response..product )
Did you try
for each (var item:XML in JavaServiceHandler(e.currentTarget).response.result.products.product )
That's how I do it. Explicit, explicit, explicit.

Try looking at JavaServiceHandler(e.currentTarget).response with a debugger. If it IS the XML you're referring to, then your method of accessing product should work. So, it's likely that your Java backend returns not the XML you're expecting.
Try loading the same XML from a simple text file, or simply embed it into a string to make it even more simple:
[Embed (source="xmltest.xml")]
private var xmlTest:String;
And then initialize XML with a var xml:XML = new XML(xmlTest); and try trace(xml..product)

The root element in your XML is result. So you should have either
for each (var item:XML in JavaServiceHandler(e.currentTarget).response.products..product )
Yes, two dots - no typo there, or (if you know for sure you'll have only product elements):
for each (var item:XML in JavaServiceHandler(e.currentTarget).response.products.children() )
Because you didn't tidy up your XML, you mistook products element for the root, as it took some time for me to notice too. The moral of the story is: always tidy up your XML.

Assign static value for DATA_LOADED in JavaFlexStoreEvent.as
public static const DATA_LOADED:String="onDataLoaded"

I Think you are not created JAvaFlexStoreEVent Here I am Posted full class
package com.wiley.jfib.store.events
{
import flash.events.Event;
public class javaFlexStoreEvent extends Event
{
public static const DATA_LOADED:String="onDataLoaded";
public function javaFlexStoreEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}
}
}

I see that you have commented out the use of http service. If you use this object and then specify the result data type of 'OBJECT', which you can access via the static variable 'HTTPService.RESULT_FORMAT_OBJECT'. This will then automatically parse your XML into objects for you. You can then use simple dot notation to access the data rather than looping over the XML.
Once you make this change, run the application using the debugger. Breakpoint your code at the result handler code and take a look at the result data. You should see that your XML has been parsed into an ArrayCollection of ObjectPoxy objects, which you can then loop over and map to your own objects for use in your application.
The benefit here is that the internal parser will handle namespaces, and most other variations of XML you throw at it, without you having to worry about it.
You can lose some performance with large data sets, but of course that only depends on how efficient you can write your own parsing code to be.

Related

Iterating through xmltextreader

I have a xml in the following format.
<?xml version="1.0" encoding="UTF-8" standalone= "yes"?>
<rss>
<report name="rpt1">
<title>AAA</title>
<image></image>
<weblink></weblink>
<pdflink></pdflink>
<pdfsize></pdfsize>
</report>
<report name="rpt2">
<title>BBB</title>
<image>CCC</image>
<weblink>DDD</weblink>
<pdflink>EEE</pdflink>
<pdfsize>FFF</pdfsize>
</report>
</rss>
Now i want to iterate this xml and get the report node and from there get childnodes like title/pdflink/size etc which would be thru. looping using for loop. I want to use xmltextreader to accompalish this. I tried using while but i get only 1 loop after iterating. I dont know why. If thru for loop how do i iterate like,
for(loop when reader.element("reports)){} and then get the rest of the nodes and put them in an array or list or so. Once i get them stored in list i would want to dipaly them ina feed. which is a best way to do this? pls help.
In my case I was worried about the performance of loading a large document. What I have done is define a constructor on my objects to receive a XmlReader and hydrate - passing the reader back after it reaches a complete node.
This allows me to yield a populated object back as IEnumerable for each object as it's being read. Then I launch a new Task/Thread to handle processing that individual item and go back to processing the file.
private IEnumerable<Report> readReports(Stream reader)
{
var settings = new XmlReaderSettings();
settings.IgnoreWhitespace = true;
var xmlReader = XmlReader.Create(reader, settings);
xmlReader.MoveToContent();
xmlReader.Read();
while (!xmlReader.EOF)
{
if (xmlReader.Name.ToUpper() == "report")
yield return new Report(xmlReader);
xmlReader.Read();
}
}
public Report(XmlReader reader) : this()
{
reader.MoveToContent();
if (reader.IsEmptyElement)
{
reader.Read();
return;
}
reader.Read();
while (!reader.EOF)
{
if (reader.IsStartElement())
{
switch (reader.Name.ToLower())
{
case "order_id":
this.OrderId = reader.ReadElementContentAsString();
break;
// abreviated the rest of the fields
default:
reader.Skip();
break;
}
}
else if (reader.Name.ToLower() == "report") //this watches for the end element of the container and returns after populating all properties
return;
}
}
I would definitly appreciate any feedback from the rest of the community, if there is a better approach or if there are any errors here please let me know.

How to have selectedColor in a colorPicker to be random

I am trying to have a random color to be picked as default for my ColorPicker. I am running my script on the creation complete of my Panel.
The script code is given below:
public var someColor:String = new String();
private function init():void{
var myColor:Number = Math.round( Math.random()*0xFFFFFF );
someColor = "0x"+myColor.toString(16);
}
and the code for the ColorPicker is:
<mx:ColorPicker id="calendar_color" width="20" height="20" selectedColor="{someColor}"/>
Even though, when I see the color in the var someColor, it shows a random color every time, but the colorPicker is not displaying the color.
Please let me know, what am I doing wrong here. And how it can be fixed.
1) Why do you have someColor as String? Therefore you've got two unnececcary type conversions: from Number to String (myColor.toString(16)) and from String to uint (selectedColor="{someColor}"). Why not just write this:
[Bindable] public var someColor:uint = 0;
private function init():void{
someColor = Math.random()*0xFFFFFF;
}
2) And one more thing I also don't like - in current answer you've got actually uneccecary binding, the one that will only shoot once, and you should not forget to call init() somewhere in youre code. I'd better change it on function call:
private function getRandomColor():uint{
return Math.random()*0xFFFFFF;
}
<mx:ColorPicker id="calendar_color" width="20" height="20"
selectedColor="{getRandomColor()}"/>
Here getRandomColor() will be automatically called on ColorPicker initialization. One unavoidable type conversion (from Number to uint), no bindings, no useless property, shorter code.
That's not premature optimization, that's removing premature pessimization.
You need to add the [Bindable] metadata:
[Bindable]
public var someColor:String = new String();
I can't tell from this post but what class is
public var someColor:String = new String();
in. you will just want to verify that it can be reached through the reference you made.

Flex 3 - Movie Clips - Viewing multiple frames on the stage

I want to view all of the frames from within a MovieClip concurrently on the stage, however I am struggling to extract the individual frame information from the MovieClip.
I have managed to load an external SWF into a MovieClip and view the frames sequentially with the following code:
<mx:HBox id="hbox"></mx:HBox>
<mx:Script>
<![CDATA[
import mx.controls.SWFLoader;
public var loader:SWFLoader = new SWFLoader();
public var myMovieClip:MovieClip = new MovieClip();
public function init():void {
loader.addEventListener(Event.COMPLETE, handleLoaded);
loader.load('assets/test_document.swf');
}
public function handleLoaded(e:Event):void {
var myMovieClip:MovieClip = e.currentTarget.content as MovieClip;
myMovieClip.stop();
hbox.addChild(loader);
}
]]>
</mx:Script>
What is the most efficient way to view ALL of the frames on the screen?
Thanks in advance.
Do you mean ALL of the frames at the same time ? like:
function getClipAsBitmaps(clip:MovieClip):ArrayCollection{
var data:ArrayCollection = new ArrayCollection();
var frames:int = clip.totalFrames;
for(var i:int = 1 ; i <= frames; i++){
clip.gotoAndStop(i);
trace(i);
var bitmapData:BitmapData = new BitmapData(clip.width,clip.height,true,0x00FFFFFF);
bitmapData.draw(clip);
data.addItem({source:new Bitmap(bitmapData),label:'frame: ' + i});
}
return data;
}
tileList.dataProvider = getClipAsBitmaps(star);
Note: MovieClip transforms are igonored
Easiest way is to just load the SWF clip.totalFrames times, gotoAndStop each instance to a different frame, and add them all to the stage in some meaningful way. Usually your browser will cache the SWF after the first load and so this isn't as horrendous as you might think.
The alternative solution is to gotoAndStop to each frame and then
var bitmapData:BitmapData = new BitmapData(clip.width, clip.height, true);
bitmapData.draw(clip);
but there are a lot of things that go wrong here -- you have to do this on an enter_frame loop instead of a for loop (otherwise you just get the 2nd frame totalFrames times, you have to have the right security settings to be able to use the draw function, if your clip has weird registration points the bitmaps will clip...

AS3: Loading bytes from multiple sources

I am loading multiple images into a class instance and I would like to keep track of how large the complete amount of loaded data is at any given point.
I have grabbed the totalbytes from my directory of images via a PHP script... but collecting the new bytes from the ProgressEvent on each image seems tricky for some reason.
Let's assume that the totalBytes variable is correct and that I am totally unfamiliar with the way that ProgressEvent works...
Does an event get fired every time ProgressEvent gets a new byte?
If not, how do I keep track of the current total bytes?
I'm sure I've got it wrong, but here's what I am trying:
public function thumbProgress(e:ProgressEvent):void
{
//trying to check for a new data amount, in case the progress event updates
//insignificantly
if(e.bytesLoaded != this.newByte)
{
this.newByte = this.currentLoadedBytes - e.bytesLoaded;
this.currentLoadedBytes += this.newByte;
this.textprog.text = this.currentLoadedBytes.toString() + " / " + this.totalBytes.toString();
this.newByte = e.bytesLoaded;
}
if(this.currentLoadedBytes >= this.totalBytes)
{
this.textprog.text = "done loading.";
}
}
Are you using a flash.display.Loader to load your images? Then you can use the Loader.contentLoaderInfo.bytesTotal property, which should contain the right number of bytes once the image has finished loading. The Loader.contentLoaderInfo property references the LoaderInfo instance of the loaded content, which contains a lot of data about the file, such as total size, the amount that have finished loading, and the URL from which it was loaded. Check out the LoaderInfo reference.
Sum the values of this property for all of your loaders, to get the total amount of loaded data, e.g. in the COMPLETE handler for each loader.
Cheers
Maybe this isn't exactly an answer to your question, but I suggest that you take a look at bulk-loader library. It will greatly simplify loading multiple assets. Here is a quick and dirty example of usage. We've got simple application with progressbar and we want to update progressbar as images are downloaded.
<mx:Script>
<![CDATA[
import br.com.stimuli.loading.BulkProgressEvent;
import br.com.stimuli.loading.BulkLoader;
private function init():void {
loadImages();
}
private function loadImages():void {
var loader : BulkLoader = new BulkLoader("main-site");
loader.add("http://www.travelblog.org/Wallpaper/pix/tb_turtle_diving_sipadan_malaysia.jpg", {id:"a"});
loader.add("http://www.travelblog.org/Wallpaper/pix/tb_fiji_sunset_wallpaper.jpg", {id:"b"});
loader.addEventListener(BulkLoader.COMPLETE, onAllLoaded);
loader.addEventListener(BulkLoader.PROGRESS, onAllProgress);
loader.addEventListener(BulkLoader.ERROR, onAllError);
loader.start();
}
private function onAllLoaded(evt : BulkProgressEvent):void {
}
private function onAllProgress(evt : BulkProgressEvent):void {
progressBar.setProgress(evt.ratioLoaded * 100, progressBar.maximum);
}
private function onAllError():void {
}
]]>
</mx:Script>
<mx:ProgressBar x="304" y="360" width="582" id="progressBar" mode="manual" minimum="0" maximum="100"/>

HTTPService Result - checking number of items with a specified name

I have a question about HTTPService and the data it returns.
Well lets consider this XML:
<PhotoGalleryData>
<Photo>
<id>1</id>
<name>Summer Vacation</name>
<description>In vacation</description>
<fullImage>originalImg/1.JPG</fullImage>
</Photo>
<Photo>
<id>2</id>
<name>Winter Vacation</name>
<description>coold</description>
<fullImage>originalImg/2.JPG</fullImage>
</Photo>
</PhotoGalleryData>
As you see i have two instances of Photo, that would be retrieved using a HTTPService, well then on the Result Event of that same HTTPService i would want him the count the amount of instances named Photo he as returned on is .lastResult.
This is a dumb question, but i can't find it anywhere in Adobe Docs.
Of course any help, hint, suggestion is greatly appreciated.
Medoix
I gotta be blind or something, because it still returns 0.
Something missing here?
MXML
<mx:HTTPService id="getData"
url="{XMLDataFileLocation}"
showBusyCursor="true"
fault="getDataFaultHandler()"
result="getDataResultHandler(event)"/>
ActionScript
import mx.controls.Alert;
import mx.rpc.events.ResultEvent;
private var xmlData:XMLList;
private var numItems:int;
private function getDataResultHandler(evt:ResultEvent):void
{
if (evt.result.PhotoGalleryData)
{
xmlData = XML(evt.result).descendants("Photo");
numItems = xmlData.length();
Alert.show('NÂș '+numItems,'num de Photo');
}
}
in the http_result function you have you will be putting this data in an XMLList for an example and then you can call the xmllist.length();
private var xmlData:XMLList;
private var numItems:Integer;
private function HttpResult(evt:ResultEvent):void {
if (evt.result.PhotoGalleryData) {
xmlData = XML(evt.result).descendants("Photo");
numItems = xmlData.length();
}
}
EDIT: Do the below...
Change
<mx:HTTPService id="getData"
url="{XMLDataFileLocation}"
showBusyCursor="true"
fault="getDataFaultHandler()"
result="getDataResultHandler(event)"/>
To...
<mx:HTTPService id="getData"
url="{XMLDataFileLocation}"
resultFormat="e4x";
showBusyCursor="true"
fault="getDataFaultHandler()"
result="getDataResultHandler(event)"/>
This is working for me.
Just do the following. it will solve your probs ;)
private var xmlData:XMLList;
private var numItems:Integer;
private function HttpResult(evt:ResultEvent):void {
if (evt.result.PhotoGalleryData) {
numItems = ArrayCollection(evt.result.PhotoGalleryData.Photo).length;
}
}
RSTanvir

Resources