Get list of Xpaths to all nodes in an XML

I’m trying to use NSXMLNode’s objectsForXQuery:error: to return a list of xPaths for nodes of a certain name OR return a list of Xpaths for all the nodes in the XML.

Option 1 example:

Using this XML:

If I just wanted all the nodes with a particular name, I could do:


use AppleScript version "2.4"
use framework "Foundation"
use scripting additions

its getNodes:"/Users/tneison/Desktop/markerTest.fcpxml" forNodesNamed:"marker"

on getNodes:thePath forNodesNamed:targetNode
	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 theNodes to theXMLDoc's objectsForXQuery:("//" & targetNode) |error|:(missing value)
	current application's NSLog("%@", theNodes)
end getNodes:forNodesNamed:

Which returns:

It would be great if there were a function that did the same thing BUT instead of returning a list of nodes, it returns a list of Xpaths like this:

Option 2 example:

Simply return a list of all the xPaths like so:

Any Ideas as to how I could accomplish either one of these tasks in ASOC?

Don’t confuse what’s logged with what you have – what’s logged is just a description. You have the nodes, so you can call the XPath method on them. If you want it as a single string, use something like this:

set theResult to ((theNodes's valueForKey:"XPath")'s componentsJoinedByString:linefeed) as text

It isn’t exactly what you asked for because it includes the index for every element:

/project[1]/sequence[1]/spine[1]/clip[1]/marker[1]
/project[1]/sequence[1]/spine[1]/clip[1]/marker[2]
/project[1]/sequence[1]/spine[1]/clip[1]/marker[3]
/project[1]/sequence[1]/spine[1]/clip[1]/marker[4]
/project[1]/sequence[1]/spine[1]/clip[2]/marker[1]
/project[1]/sequence[1]/spine[1]/clip[2]/marker[2]
/project[1]/sequence[1]/spine[1]/clip[2]/marker[3]

But you can get rid of them easily enough:

set theResult to ((theNodes's valueForKey:"XPath")'s componentsJoinedByString:linefeed)
set theResult to (theResult's stringByReplacingOccurrencesOfString:"[1]" withString:"") as text
  -->	"/project/sequence/spine/clip/marker
/project/sequence/spine/clip/marker[2]
/project/sequence/spine/clip/marker[3]
/project/sequence/spine/clip/marker[4]
/project/sequence/spine/clip[2]/marker
/project/sequence/spine/clip[2]/marker[2]
/project/sequence/spine/clip[2]/marker[3]"

Wow. Thanks. That’s pretty easy. I keep forgetting that the array returned is the actual locations in the XMLDocument object. Since that is the case I really don’t need to return the xPath. If I want to modify the value of , I can simply do this:


use AppleScript version "2.4"
use framework "Foundation"
use scripting additions

its editNodeWithName:"marker" atIndex:0 withAttrNamed:"value" replacementString:"This is a new value" inputPath:"/Users/tneison/Desktop/markerTest.fcpxml" saveResultTo:"/Users/tneison/Desktop/markerTest-RESULT.fcpxml"

on editNodeWithName:targetNode atIndex:|index| withAttrNamed:theAttrName replacementString:newString inputPath:thePath saveResultTo:outputPath
	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 theNodes to theXMLDoc's objectsForXQuery:("//" & targetNode) |error|:(missing value)
	set theTag to theNodes's objectAtIndex:|index|
	set theAttr to theTag's attributeForName:theAttrName
	theAttr's setStringValue:newString
	set theXMLData to theXMLDoc's XMLDataWithOptions:(current application's NSXMLNodePrettyPrint)
	theXMLData's writeToFile:outputPath atomically:true
end editNodeWithName:atIndex:withAttrNamed:replacementString:inputPath:saveResultTo:

That’s right. If you want to detach them, you have to use the detach method.