Parse XML into table using 'System Events': A scope problem?

I receive student rosters in the form of xml files from a university student information system. They recently switched to an xml output rather than tab-delimited export format. I’d like to be able to read this xml straight into an AS Studio app table so I can use it further.

I figured out how to have a plain AppleScript parse the file using System Events, so I figured it would be easy to use this code in AS Studio. I used an existing document-based AS Studio app that reads in tab delimited rosters, again thinking it would be simple to adapt that to this new xml data format. Well, that was three days ago, and I have to admit that this simple thing has become an obession–you know there is nothing worse than something that should be simple!

I’ve scoured the forums under ‘xml,’ ‘read xml,’ ‘parse xml,’ ‘xml to tables,’ and ‘system events.’ Nothing seems to solve this thorny problem. I’m also not sure if NSArrays would be of any help–I’m not versed in call methods, etc. I’d like to avoid 3rd party solutions since these inevitably go away, and besides, I like the home-grown solutions, and ‘System Events’ seems like a nutural!

Here is the code for an ‘Import’ button, with comments I’ve inserted. It is meant to be readable, so I hope the problem is clear. (You can tell that I have plagarized all sorts of things from this forum, and the doc-based code is from an AS Studio example. I am sure the basics are familiar to folks in this forum!)

A sample xml file:

<?xml version="1.0" encoding="utf-8" standalone="yes"?> bondicja Bondicova Jenny bond@.edu

on clicked theObject
	set theWindow to window 1 of theObject
	set theDataSource to data source of table view "roster" of scroll view "roster" of theWindow
	
	if name of theObject is equal to "import" then
		--Begin by getting the roster file
		choose file with prompt ("Select an XML file with your roster") without invisibles
		set xmlFile to result as Unicode text
		
		tell application "System Events"
			tell contents of XML file xmlFile
				set theXMLDB to the name of XML element 1 --this returns the main element, which should have title data of the xml file
				set theRecordCount to the count of XML elements of XML element theXMLDB --this returns the next level of the XML, all of the 'records'
				
				--the parsing can begin here
				repeat with theRecord from 1 to theRecordCount
					--I would think the XML file could be read straight into the table at this point...
					--but it appears to have a scope problem.
					--[b]When run, the following line produces an error...
					-->System Events got an error: Can't make every row of contents of XML file "~:Desktop:XML stuff:RosterXML.xml" into type reference. (-1700)[/b]					set theRow to make new data row at the end of data rows of theDataSource
					--  [b]The following line prevents a compile!  I assume for the same reason as above, but then how do I get this into a table?[/b]
					-->set contents of data cell "studentID" of theRow to (the value of (XML element 1 of XML element theRecord of XML element theXMLDB))
					-->further data cells would go here...
				end repeat
			end tell
		end tell
	end if --END IMPORT
end clicked

The problem seems straightforward. I have an xml file I wish to use as a data source for an AS Studio table. There are numerous examples of reading xml files using third party additions, but I think the ‘System Events’ solution sounds like a long-term winner. The problem is that to use ‘System Events’ one needs to refer to ‘the contents of the XML file’, and this creates a scope problem in AS Studio. Simply put, the tell command for ‘the contents of the XML file’ will not allow one to create a data row in AS Studio. So, as one happily loops through the data elements of an xml file, one cannot create table rows to contain the data. One can only create a data table row in the main AS Studio handler–‘on clicked’ for example. A problem of the scope of the system event.

I know I can’t be the only person with this problem/need/desire, so I will put my kludge here. Hopefully someone wise in the ways of AS Studio will offer a better solution.

First–for those like me who know very little about xml–know thy data file. One can find a handy element reader in the forum, and it would also be nice to read up on XML at this web site A Technical Introduction to XML Suffice to say, a few nested ‘name of element of…’ statements later, and you will be able to read the actual data in the file with a ‘value of the elements of…’ statement. Parsing is not the problem. As I’ve mentioned, the problem will be when you transport your nifty script into AS Studio. The trick there will be to break the problem into two pieces. First, use a handler to get the number of ‘record’ elements from the xml–these are the ones that contain the data elements. (continued below the sample…)

--the definition of the file itself. records will be contained inside these tags. it can actually be called anything, not just .
--the data will be inside these. again, the name is not critical.
--actual data elements are in their own tags as well.

SAMPLE: You will need to first count the number of ‘record’ tags. this can be done using system events











Once you have a count of these, you need to return that value to your main handler. Using that number, set up a repeat loop form 1 to the count of the records. Within the repeat loop you can create a new data row, then use another handler to get the row from the xml by simply passing the current ‘repeat’ variable. Use that handler to get that record data from the xml and pass it back to the main handler, which puts it into the AS Studio table. The following example might help…


on clicked theObject --some import button in AS Studio
	set theWindow to window 1 of theObject
	set theDataSource to data source of table view "roster" of scroll view "roster" of theWindow  --a list of students, perhaps?

choose file without invisibles --select a file to import
set xmlFile to result as Unicode text
set numberOfRecords to my getNumber (xmlFile) --go to the handler to count records
		repeat with i from 1 to numberOfRecords --repeat using this scope to set the record number
			set thisRecord to my getRecord(xmlFile, i) --pew.  use a a handler to get the record
			set theRow to make new data row at the end of data rows of theDataSource --this works here, but would not if contained in a 'tell contents of XML file xmlFile' statement
			set contents of data cell "studentID" of theRow to (text item 1 of thisRecord)
			set contents of data cell "lastName" of theRow to (text item 2 of thisRecord)
			set contents of data cell "firstName" of theRow to (text item 3 of thisRecord)
			set contents of data cell "emailAddress" of theRow to (text item 4 of thisRecord)
		end repeat
		-->End getting file
	end if -->END IMPORT
end clicked

on getNumber(xmlFile)
	tell application "System Events"
		tell contents of XML file xmlFile
			set theXMLDB to the name of XML element 1 --this returns the main element the ONE that contains all records
			set theRecordCount to the count of XML elements of XML element theXMLDB --this returns the next level of the XML, in this example, the 'records'.  further element gets may be necessary
		end tell
	end tell
	return (theRecordCount)
end getRecordCount

on getRecord(xmlFile, i)
	set myRecord to {}
	tell application "System Events"
		tell contents of XML file xmlFile
			set theXMLDB to the name of XML element 1 --similar to the previous handler
			
			tell XML element i of XML element theXMLDB
				set myRecord to {(value of XML element "studentID"), (value of XML element "lastName"), (value of XML element "firstName"), (value of XML element "emailAddress")}
			end tell
			
		end tell
	end tell
	return (myRecord)--pass the list back to the main handler to be put in the table
end getRecord

A kludge, I know, I know!! But for now, it works. I hope this helps anyone in the same boat.