ASObjC Runner Got an error: Connection is invalid

I am using ASObjC Runner for its XML parsing capabilities (among other things). I’m getting a strange result when trying to use the ‘extract from XML’ command: ASObjC Runner got an error: Connection is invalid.

The command I was using was:

tell "ASObjC Runner" to set oneValue_asList to extract from XML someXML matching nodeXPath expression {oneKeyName} without including output element

where someXML was a valid block of XML, nodeXPath was an XPath to the desired node, and oneKeyName was the item whose value I wanted to read. I do this for many different key names and many different XPaths. It is hard to debug because it seems to get this error at different points every time I run the whole script. I haven’t discovered a particular key name or XPath that causes the error. One that fails once might succeed the next time, without any change to the underlying XML or key name and XPath choices.

I don’t see anything online that would explain this. I am running this in Yosemite, 10.10.1, and have the most recent version of AsObjC Runner. Any ideas on what might be causing this? The XML being processed is fairly large. I’ve even tried quitting AsObjC Runner periodically during the script to see if it is leaking memory. That seems to have no effect.

Correction: it seems like quitting AsObjC Runner often does somewhat mitigate this problem.
We tried modifying the script so that it tells AsObjC Runner to quit before every time we call the ‘extract from XML’ command. We were able to get through a run of the entire script, but only on the second try. The first time it got fairly far through, but eventually got the error. So, it seems like it isn’t a complete fix for this error.

Is there some memory limit for AsObjC Runner? Would it help to split my large XML data into separate pieces? Any other ideas or information I can collect to help with figuring out a solution? I’m hoping Shane Stanley sees this post and has some thoughts.

On one hand, I’m a bit surprised. Unless the XPath expression potentially returns a huge number of results, there should be no problem with limits. I presume you’re running v1.9.15. And I know several people are still using it under 10.10, specifically for the XML stuff.

But it really is time to start moving away from ASObjC Runner – better to do it now while it’s still working, than waiting for it to break (completely).

Is there some reason you’re not using a “native” ASObjC solution? This should get you started:

use framework "Foundation"

on extractFrom:thePath matchingXPath:theQuery
	set aURL to current application's |NSURL|'s fileURLWithPath:thePath -- make NSURL
	set theXMLDoc to current application's NSXMLDocument's alloc()'s initWithContentsOfURL:aURL options:0 |error|:(missing value) -- make XMLDoc
	set attStrings to {} -- where attributes will be stored
	set theXMLOutput to current application's NSXMLElement's alloc()'s initWithName:"output" -- found nodes are added to this
	set {theResults, theError} to (theXMLDoc's nodesForXPath:theQuery |error|:(reference)) -- query
	if theResults is not missing value then
		repeat with aNode in (theResults as list)
			aNode's detach() -- need to detach first
			if aNode's |kind|() as integer = current application's NSXMLAttributeKind then -- see if it's an attribute or node
				set end of attStrings to (aNode's stringValue()) as text
				(theXMLOutput's addChild:aNode) -- add node
			end if
		end repeat
		return {(theXMLOutput's XMLStringWithOptions:(current application's NSXMLNodePrettyPrint)) as text, attStrings} -- change options to suit
		return missing value
	end if
end extractFrom:matchingXPath:

Thanks, Shane!
I took your advice and we re-worked all of our uses of AsObjC Runner to use the framework instead, since we can control which machine this runs on and can stick to Yosemite. We actually tweaked your example to create an NSXMLDocument from content instead of a file, since we often have snippets of XML already loaded into AppleScript variables. We also added some features to strip off the node’s own tag (to get just the content or value of a node, as well as all the other parts we needed. It’s been an interesting dive into the Cocoa documentation for an AppleScript/FileMaker programmer.

One interesting thing we ran into is that, in an AppleScript script that has use framework “Foundation” in it, certain Standard Additions commands are unusable. That prevented us from using do shell script to log to console certain interstitial variables while debugging. I suppose we’ll have to move a lot of code that uses those functions into other libraries, and stick to very vanilla AppleScript when we ‘use frameworks.’

Thanks again!

Just add a “use scripting additions” statement, and you should then be able to use the scripting addition commands.

But for logging to console, you can also use NSLog, like this:

current application's NSLog("%@", someVariable)

There are some gotchas, but basically %@ gets replaced by the variable’s value. If there are numbers, you have to use %d or %f. So:

use framework "Foundation"
set a to "Some string"
set b to {1, 2, 3}
set c to 5
set d to 3.5
current application's NSLog("Logging %@ and %@ and %d and %f", a, b, c, d)

will log this:

25/01/2015 2:08:43.657 pm ASObjC Explorer 4[8008]: Logging Some string and (
) and 5 and 3.500000

Great advice, as usual - thanks!
I’ll probably wrap whatever I want to log in a handler I use often called coerceToString. It’s a little hacky, using AppleScript’s error message returned when trying to coerce a variable into a number to extract an exact string representation of the variable’s value. That means that this is an English-specific function:

on coerceToString(incomingObject)
	-- version 1.8, Daniel A. Shockley,
	-- 1.8 - instead of trying to store the error message used, generate it
	-- 1.7 -  added "Can't make " with a curly single-quote. 
	-- 1.6 -  can add additional errMsg parts (just add to lists to handle other languages. 
	--             Currently handles English in both 10.3 and 10.4 (10.3 uses " into a number." 
	--             while 10.4 uses " into type number.")
	-- 1.5 -  added Unicode Text
	-- 1.0 - based on ideas/advice from, etc
	set errMsgLeadList to {"Can't make ", "Can't make "}
	set errMsgTrailList to {" into a number.", " into type number."}
	if class of incomingObject is string then
		set {text:incomingObject} to (incomingObject as string)
		return incomingObject
	else if class of incomingObject is integer then
		set {text:incomingObject} to (incomingObject as string)
		return incomingObject as string
	else if class of incomingObject is real then
		set {text:incomingObject} to (incomingObject as string)
		return incomingObject as string
	else if class of incomingObject is Unicode text then
		set {text:incomingObject} to (incomingObject as string)
		return incomingObject as string
		-- LIST, RECORD, styled text, or unknown
				"XXXX" as number
				-- GENERATE the error message for a known string so we can get 
				-- the 'lead' and 'trail' part of the error message
			on error errMsg number errNum
				set {oldDelims, AppleScript's text item delimiters} to {AppleScript's text item delimiters, {"\"XXXX\""}}
				set {errMsgLead, errMsgTrail} to text items of errMsg
				set AppleScript's text item delimiters to oldDelims
			end try
			set testMultiply to 1 * incomingObject -- now, generate error message for OUR item
			-- what items is THIS used for?
			-- how does script ever get past the above step??
			set listText to (first character of incomingObject)
		on error errMsg
			--tell me to log errMsg
			set objectString to errMsg
			if objectString contains errMsgLead then
				set {od, AppleScript's text item delimiters} to {AppleScript's text item delimiters, errMsgLead}
				set objectString to text item 2 of objectString
				set AppleScript's text item delimiters to od
			end if
			if objectString contains errMsgTrail then
				set {od, AppleScript's text item delimiters} to {AppleScript's text item delimiters, errMsgTrail}
				set AppleScript's text item delimiters to errMsgTrail
				set objectString to text item 1 of objectString
				set AppleScript's text item delimiters to od
			end if
			set {text:objectString} to (objectString as string)
		end try
		return objectString
	end if
end coerceToString