Cocoa will easily convert NSDictionary objects to AppleScript (AS) records and vice versa for you, you only need to tell how to do it.
First of all, you need to define the record-type in the script definition file ( .sdef ), for example
<record-type name="http response" code="HTRE"> <property name="success" code="HTSU" type="boolean" description="Was the HTTP call successful?" /> <property name="method" code="HTME" type="text" description="Request method (GET|POST|...)." /> <property name="code" code="HTRC" type="integer" description="HTTP response code (200|404|...)." > <cocoa key="replyCode"/> </property> <property name="body" code="HTBO" type="text" description="The body of the HTTP response." /> </record-type>
name is the name that will have a value in the AS record. If the name is equal to the NSDictionary key, then the <cocoa> tag is not required ( success , method , body in the above example), if not, you can use the <cocoa> tag to tell Cocoa the correct key to read this value (in the above example code is the name in the AS record, but in NSDictionary there will be replyCode , I just did it for demo purposes here).
It is very important that you tell Cocoa that the AS type must have this field, otherwise Cocoa does not know how to convert this value to an AS value. All values ββare optional by defualt, but if present, they should be of the expected type. Here's a short table of how the most common Foundation types correspond to AS (incomplete) types:
AS Type | Foundation Type -------------+----------------- boolean | NSNumber date | NSDate file | NSURL integer | NSNumber number | NSNumber real | NSNumber text | NSString
See Apple Table 1-1 , βAn Introduction to Cocoa Scripting Guideβ
Of course, the value itself may be another nested entry, just define a record-type for it, use the name record-type in the property specification, and in NSDictionary value should be a suitable dictionary.
Ok, try the full sample. Let us define a simple HTTP get command in our .sdef file:
<command name="http get" code="httpGET_"> <cocoa class="HTTPFetcher"/> <direct-parameter type="text" description="URL to fetch." /> <result type="http response"/> </command>
Now we need to implement this command in Obj-C, which is simple:
Of course, returning nil in case of internal failures is an incorrect error handling. Instead, we could return an error. Well, there are even special error handling methods for AS that we could use here (for example, setting some properties that we inherited from NSScriptCommand ), but this is just a sample after all.
Finally, we need the AS code to verify it:
tell application "MyCoolApp" set httpResp to http get "http://badserver.invalid" end tell
Result:
{success:false}
As expected, now the one that succeeds:
tell application "MyCoolApp" set httpResp to http get "http://stackoverflow.com" end tell
Result:
{success:true, body:"<!DOCTYPE html>...", method:"GET", code:200}
As expected.
But wait, you wanted everything to be the other way around, right? Okay, try this. We just reuse our type and make another command:
<command name="print http response" code="httpPRRE"> <cocoa class="HTTPResponsePrinter"/> <direct-parameter type="http response" description="HTTP response to print" /> </command>
And we also implement this command:
And we check it:
tell application "MyCoolApp" set httpResp to http get "http://stackoverflow.com" print http response httpResp end tell
And her application is registered in the console:
Dictionary is { body = "<!DOCTYPE html>..."; method = GET; replyCode = 200; success = 1; }
So this, of course, works both ways.
Well, now you can complain that this is not arbitrary, because you need to determine what keys (can) exist and what type they will have, if they exist. You're right. However, as a rule, the data is not arbitrary, I mean that after the code must be able to understand, and therefore it must at least follow certain rules and patterns.
If you really have no idea what data to expect, for example. as a dump tool that simply converts two well-defined data formats without any understanding of the data itself, why do you pass it as a record at all? Why don't you just convert this entry to a simple syntactic string value (like a list of properties, JSON, XML, CSV), and then pass it to Cocoa as a string and finally convert back to objects? This is a simple but very powerful approach. Cocoa's list of Parsing or JSON properties is done with perhaps four lines of code. Well, perhaps this is not the fastest approach, but anyone who mentions AppleScript and high performance in one sentence has already made a fundamental mistake to start with; AppleScript, of course, can be many, but "fast" is none of the properties you can expect.