Tuesday, December 22, 2009

Active Reports 6 - XML Data as a Datasource

This week my challenge was to use XML data as a datasource for an Active Report. I'm pretty new to Active Reports. For the most part, I don't really like any reporting tools, but that is probably because I want it to do more that it can.

So when I figured out I needed to use XML for a certain report, I hit the Active Reports forum to look for examples. Most examples showed files with XML as a datasource. You hook up the datasource object to the file location. However, I had straight XML data that I was dynamically pulling from the database so I needed to hook up the Xmldatasource object to that data.

My report was a Master-Detail report, and I was supplying the xml data to the subreport. And of course, this is in C#.

The trick was loading the xml into an XmlDocument. The other trick was figuring out the RecordSetPattern, that took a few guesses. Looking back it's clear.

Sample XML data:

<books userId="1157" bId="1001">
<book id="3333" majorVersion="2" minorVersion="0">
<title>Junk</title>
< rating positive="6" possible="10" percent="40" />
</book>
<book id="4444" majorVersion="1" minorVersion="0">
<title>My Module</title>
<rating positive="5" possible="10" percent="50" />
</book>
</books>

//getting data from database as xml
XElement booksXml = User.GetBooks(member.ID, _b.ID, 0);

//create an xml datasource for the subreport
DataDynamics.ActiveReports.DataSources.XMLDataSource xmlDS = new DataDynamics.ActiveReports.DataSources.XMLDataSource();

//load data
XmlDocument xDoc = new XmlDocument();
xDoc.LoadXml(booksXml.ToString());
xmlDS.FileURL = null;
xmlDS.RecordsetPattern = "//book";
xmlDS.LoadXML(xDoc.InnerXml);

//set subreport datasource
this.subReport1.Report = new BookDetail();
this.subReport1.Report.DataSource = xmlDS;


And beyond that the final trick was referencing the xml data in the subreport. For any attribute that was in module, I simply referenced the attribute name in the datafield property with that @ symbol in front of it (like @id), and for elements just the element name (like title). The tricky part was the nested node inside there, rating. To reference an attribute from grade, I had to put ./rating/@percent in the datafield.

And I also found that trying to run a calculation to figure that percent was beyond me, so I just modified my xml query to include calculating the percent for me, it just seemed easier. And that's what I mean when I say I don't like reporting tools...

Wednesday, December 9, 2009

ActionScript3 Xml - Update nested element

Today, I had yet another XML challenge in ActionScript. Previously, I needed to update a child that was directly under the root. This time I needed to update a child that was deeply nested. This was pretty tricky and the answer was my last straw before pulling out my hair. As a last ditch effort, I used the equal sign, instead of using the replace function. This time I just could not get the replace function to do what I wanted it to do. I'm not sure why, but I finally have a working solution that I'm happy with.

sample data:

<root>
<category id="1">
<books>
<book id="1" name="junk"/>
<book id="2" name="junk2"/>
</books>
</category>
</root>

var varCategoryId:int = 1;
var varBookId:int = 2;
var updateXMl = "";

var i:int = rootXML..category.(@id == varCategoryId)..book.(@id == varBookId).childIndex();

if(i > -1)
rootXML..category.(@id == varCategoryId)..book[i] = updateXml;

Hope that helps someone!

Thursday, December 3, 2009

ActionScript3 - Update XML Data

I generally avoid XML data, it can be cumbersome to navigate, however, in my current project we use XML extensively. So I've had to deal with it.

I kind of have the parsing and filtering of XML data down...kind of. I still think a collection of objects is much simpler to handle.

My new task this week was to figure out if it was possible to update a set of XML data using ActionScript3. After googling it, I finally found the answer, yes, at least this seems to be working so far.

The XML class has replace function, or method whatever term you would like to use. It takes two arguments/parameters, the first identifies with piece of XML to replace the second is the replacement data.

So one option with the replace function is to pass in the index of the data/node you want to update as the first argument. This is the option I chose.

At first, the only way I could figure out how to calculate the index of the XML node I wanted to update was to use a for loop. However, I did more digging and discovered the childIndex() property/function of the XML class. So from there it was easy peasy.

Here is the beautiful code that brought me momentary JOY:
Sample Data:
<books>
<book id="1" title="Junk"></book>
<book id="2" title="More Junk"></book>
<book id="3" title="More More Junk"></book>
</books>

private function junk(data:Object, id:int):void{

//new data
var updatedBook:XML = data as XML;

//filter XML, and get index
var i:int = (_model.user.BooksXML..book.(@id == id).childIndex());

//-1 means no match, i assume, otherwise replace xml node at specified index
if(i > -1)
_model.user.BooksXML.replace(i, updatedBook);

}

Okay so hope that helps someone.

Now, if you need to good quick recipe, I recommend Rachael Ray's ( I LUV her) Chicken Dumping Stoup. Yum, I tried it the other nite, it was yummy and the fam liked it.