I’d like to be able to modify xml values using NSXMLDocument, but I’m struggling to understand how to do so.
Here’s an example:
What if I want to replace with ?
Thanks to Shane Stanley’s Everyday AppleScriptObjC, I know how to initialize an instance of the xml:
use AppleScript version "2.4"
use framework "Foundation"
use scripting additions
set thePath to "/users/tneison/desktop/test.xml"
its extractFrom:thePath
on extractFrom:thePath
set theURL to current application's NSURL's fileURLWithPath:thePath
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
current application's NSLog("%@", theXMLDoc)
end extractFrom:
I can create a new node using:
set newNode to current application's NSXMLNode's elementWithName:"clip" stringValue:" name=\"A test project\""
but this not is not formated in the same way. The first part of the node is suppose to be an open tag like this:
But instead I get this:
Furthermore, I don’t know the simplest way to replace the old node with the new one. It seems that this should do it but it returns index beyond bounds
set theXMLDocMod to theXMLDoc's replaceChildAtIndex:5 withNode:newNode
There are several ways, depending on what you want to achieve. You can detach the old node, and add a new one using addChild:, or replace it.
But instead I get this:
[/quote]
You’re confusing the node’s stringValue with an attribute. You need something like this:
set newAttr to current application's NSXMLNode's attributeWithName:"name" stringValue:"A test project"
set newNode to current application's NSXMLNode's elementWithName:"clip" children:(missing value) attributes:{newAttr}
That’s the right command, but wrong target and index. Your is child 0 of the element – it’s not a (direct) element of the document. You need to either walk the node hierarchy or use nodesForXPath:error: to get the node first.
I’m still stuck on replaceChildAtIndex:withNode: you said:
So I tried this:
use AppleScript version "2.4"
use framework "Foundation"
use scripting additions
set thePath to "/users/tneison/desktop/test.xml"
its extractFrom:thePath
on extractFrom:thePath
set theURL to current application's NSURL's fileURLWithPath:thePath
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 newAttr to current application's NSXMLNode's attributeWithName:"name" stringValue:"A test project"
set newNode to current application's NSXMLNode's elementWithName:"clip" children:(missing value) attributes:{newAttr}
set theSpine to theXMLDoc's nodesForXPath:"/fcpxml/library/event/project/sequence/spine" |error|:(missing value)
set theSpineMod to theSpine's replaceChildAtIndex:0 withNode:newNode
current application's NSLog("%@", theSpineMod)
end extractFrom:
I’ve managed to extract the and place the new in but I can’t figure out how to put it all back together.
use AppleScript version "2.4"
use framework "Foundation"
use scripting additions
set thePath to "/users/tneison/desktop/test.xml"
its extractFrom:thePath
on modifyXML:thePath
set theURL to current application's NSURL's fileURLWithPath:thePath
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 newAttr to current application's NSXMLNode's attributeWithName:"name" stringValue:"this is a test"--build the attribute
set newNode to current application's NSXMLNode's elementWithName:"clip" children:(missing value) attributes:{newAttr}--put the attribute in a new node
set theSpineArray to theXMLDoc's nodesForXPath:"/fcpxml/library/event/project/sequence/spine" |error|:(missing value)--extract the <spine> (the parent of <clip>)
set theSpine to theSpineArray's objectAtIndex:0--extract the nodes from the array
theSpine's replaceChildAtIndex:0 withNode:newNode--replace <clip>
set theClipChildrenArray to theXMLDoc's nodesForXPath:"/fcpxml/library/event/project/sequence/spine/clip/video" |error|:(missing value)-- extract the nodes below <clip>
theSpine's insertChildren:theClipChildrenArray atIndex:1--restore the nodes below <clip>
current application's NSLog("%@", theSpine)
end modifyXML:
I’m not clear exactly what you’re after. When you use replaceChildAtIndex::, you replace one item with another – and that includes any elements of the replaced item. But now I’m wondering if all you want to do is change the name attribute of the element. Can you clarify what you want to do?
Yes. Ultimately I probably won’t need to change the structure of the XML tree just the attributes of individual nodes. I’m guessing there is probably a much simpler way to do that.
Use XPath to find the , then change the stringValue of its name attribute. Something like:
set theClipArray to theXMLDoc's nodesForXPath:"/fcpxml/library/event/project/sequence/spine/clip" |error|:(missing value)
set theClip to theClipArray's objectAtIndex:0
set theAttr to theClip's attributeForName:"name"
theAttr's setStringValue:"this is a test"