asxpath - Executing XPath expressions against an XML document

For one of my latest scripting projects at work I needed AppleScript to quickly parse a small XML file for certain informations.

As you know, there are already wonderful scripting additions available for this task, like the XML suite from Satimage or the XSLT Tools from Late Night Software, but unfortunately my script was supposed to work out of the box on every Mac in the company and so I couldn’t use them, as scripting additions need to be installed first.

What to do? Well, finally I wrote my own little and very simple foundation tool, which enables AppleScript to execute XPath expressions against an XML document using the excellent NSXMLNode class provided by Apple. The small tool can be included with the script/application bundle and there is no need to install a scripting addition first.

I know that asxpath might only be of minor interest for other people, but it works just fine and flawless in our project and so I decided to publish it here :slight_smile:

asxpath - Execute XPath expressions against XML documents (ca. 5 KB)

asxpath requires Mac OS X 10.4 or later and was successfully tested on several Intel and Power-PC based Macs.

In case you want to adjust asxpath to your very own requirements I invite you to take a look at the most simple source code.

To test-drive asxpath on your fine Mac I wrote some sample code which utilizes asxpath to search the iTunes Music Library XML file for tracks of a certain artist. The demo assumes that you put asxpath on your desktop:

property mytitle : "asxpath demo"

-- I am searching the iTunes Music Library XML files for tracks by Steve Martin
-- using the asxpath command line tool
on run
		-- asking the user to provide an artist name
		set artist to my askforartist()
		if artist is missing value then
		end if
		-- path to the asxpath foundation tool (assuming that it is loacted on the desktop)
		set foundtoolpath to quoted form of POSIX path of ((path to desktop as text) & "asxpath")
		-- path to the iTunes Music Library XML file
		set xmllibrarypath to quoted form of POSIX path of ((path to music folder as text) & "iTunes:iTunes Music Library.xml")
		-- an XPath expression searching for tracks by Steve Martin (you might want to adjust this)
		set xpathexpression to quoted form of ("//string[preceding-sibling::key[1] = \"Name\" and following-sibling::string = \"" & artist & "\"]/text()")
		-- setting the delimiter which separates the results from each other
		-- you don't need to set it, it is optional, because asxpath uses "..." as the default delimiter
		set delim to "@@@"
		-- executing the command line tool
		set command to foundtoolpath & " -file " & xmllibrarypath & " -xpath " & xpathexpression & " -delim " & delim
		-- depeding on the size of your iTunes library, this may take some seconds...
		-- remember, it's just a demo :-)
		set output to do shell script command
		-- splitting the output into usable reults
		set tracknames to my gettxtitems(return & delim & return, output)
		-- displaying the results
		choose from list tracknames with prompt "All tracks in iTunes from " & artist & ":" with title mytitle
		-- returns the following list on my Mac for artist 'Steve Martin':
		-- {"I'm Feelin' It", "Philosophy / Religion / College / Language (Album Version)",
		-- "Creativity In Action / I'm In the Mood for Love", "A Wild and Crazy Guy",
		-- "A Charitable Kind of Guy", "An Exposé", "Cat Handcuffs", "You Naive Americans",
		-- "My Real Name", "King Tut"}
	on error errmsg number errnum
		if errnum is not equal to -128 then
			my dsperrmsg(errmsg, errnum)
		end if
	end try
end run

-- I am asking the user to provide an artist name
on askforartist()
		tell me
			display dialog "I want to search iTunes for tracks by artist:" default answer "Steve Martin" buttons {"Cancel", "OK"} default button 2 with icon note with title mytitle
			set dlgresult to result
		end tell
	on error
		return missing value
	end try
	set usrinput to text returned of dlgresult
	if usrinput is "" then
		my askforartist()
		return usrinput
	end if
end askforartist

-- I am returning the text items of a text separated by the given delimiter
on gettxtitems(delim, txt)
	set olddelims to AppleScript's text item delimiters
	set AppleScript's text item delimiters to {delim}
	set txtitems to text items of txt
	set AppleScript's text item delimiters to olddelims
	return txtitems
end gettxtitems

on dsperrmsg(errmsg, errnum)
	tell me
		display dialog "Sorry, an error occured:" & return & return & errmsg & return & "(" & errnum & ")" buttons {"OK"} default button 1 with icon stop with title mytitle
	end tell
end dsperrmsg

Happy Scripting!

Hi Martin,

great solution.
I recommend to take the path to the iTunes music folder from the iApps preference file

set xmllibrarypath to text 3 thru -3 of (do shell script "/usr/bin/defaults read iTunesRecentDatabasePaths | /usr/bin/cut -d '\"' -f 2")

I’m wondering whether parsing the XML file is faster than Scripting Bridge

How do you know this!? Excellent information! Thanks!

The execution of XPath expressions can be quite slow with many elements in a large document (like the iTunes Library XML file…), so I guess the Scripting Bridge is faster. But for quickly retrieving structured info from an XML document, using XPath expressions can be quite convenient, as you don’t have to traverse the complete node tree.

Enjoy the Sunday!

It’s funny I found this today. Just yesterday I was learning this technique in objective-c. Now I would say that your above statement is wrong. This technique can be used to parse html files too (not just xml files). Lots of people on here use curl to get html code and then do some parsing. In the past I always used string techniques to parse out my information… but after learning about xpath I now know there’s an easier way. I haven’t tried your tool yet but I assume people could use it to parse html.

FYI: here’s the tutorial I found about parsing html code with xpath…