Archive for the '構造化データ処理, XML, plist, JSON' Category

2017/01/05 XPathQuery4ObjCのじっけん

オープンソースのXML/RSSのパーサー「XPathQuery for Objective-C」(By Satoshi Konno)をCocoa FrameworkにビルドしてAppleScriptから呼び出してみました。

いくつも試してはみたものの、なかなかしっくりくるRSSのパーサーがなかったのですが、これまで試した中では一番いい感じです(当社比)。

とりあえず、OS X 10.10以降で動作するようにXPathQuery4ObjCをFramework化してビルドしたバイナリを用意しておきました。自己責任でframeworkを~/Library/Frameworksフォルダ以下にアーカイブを展開してコピーしたうえで本Scriptをおためしください。

–> Download Framework Binary

AppleScript名:XPathQuery4ObjCのじっけん
– Created 2017-01-05 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “XPathQueryKit”
–https://github.com/cybergarage/XPathQuery4ObjC
–http://piyocast.com/as/archives/4376

set rssURL to current application’s |NSURL|’s URLWithString:“http://rss.news.yahoo.com/rss/topstories”
set xpathQuery to current application’s CGXPathQuery’s alloc()’s initWithContentsOfURL:rssURL

if ((xpathQuery’s parse()) as boolean) = true then
  set aTitle to xpathQuery’s valueForXPath:“/rss/channel/title”
  
set entriesList to xpathQuery’s objectsForXPath:“/rss/channel/item”
  
  
repeat with xpathObject in entriesList
    
    
set entryTitle to (xpathObject’s valueForXPath:“title”)
    
log {entryTitle as string}
    
    
set linkURL to (current application’s |NSURL|’s URLWithString:(xpathObject’s valueForXPath:“link”))
    
log {linkURL’s absoluteString() as string}
    
    
set mediaContent to (xpathObject’s objectForXPath:“media:content”)
    
    
if mediaContent is not equal to missing value then
      set anAttr to mediaContent’s attributes()
      
set anImageURL to (current application’s |NSURL|’s URLWithString:((mediaContent’s attributes())’s valueForKey:“url”))
      
log {anImageURL’s absoluteString() as string}
    end if
    
  end repeat
  
end if

★Click Here to Open This Script 

AppleScript名:XPathQuery4ObjCのじっけん2
– Created 2017-01-05 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “XPathQueryKit”
–https://github.com/cybergarage/XPathQuery4ObjC
–http://piyocast.com/as/archives/4376

set rssURL to current application’s |NSURL|’s URLWithString:“http://piyocast.com/as/feed”
set xpathQuery to current application’s CGXPathQuery’s alloc()’s initWithContentsOfURL:rssURL

if ((xpathQuery’s parse()) as boolean) = true then
  set entriesList to xpathQuery’s objectsForXPath:“/rss/channel/item”
  
  
repeat with itemObject in entriesList
    set aChild to itemObject’s children()
    
log aChild
    
    
set aTitle to (itemObject’s valueForXPath:“title”)
    
log aTitle as string
    
    
set aLink to (itemObject’s valueForXPath:“link”)
    
log aLink as string
    
    
set aComments to (itemObject’s valueForXPath:“comments”)
    
log aComments as string
    
    
set aPubdate to (itemObject’s valueForXPath:“pubDate”)
    
log aPubdate as string
    
    
set aCreator to (itemObject’s valueForXPath:“dc:creator”)
    
log aCreator as string
    
    
set aCategory to (itemObject’s valuesForXPath:“category”)
    
log aCategory as list
    
    
set aGUID to (itemObject’s valuesForXPath:“guid”)
    
log aGUID as string
    
    
set aDesc to (itemObject’s valueForXPath:“description”)
    
log aDesc as string
    
    
set aCommentURL to (itemObject’s valueForXPath:“wfw:commentRss”)
    
log aCommentURL as string
  end repeat
  
end if

★Click Here to Open This Script 

2016/11/30 Yahoo! 形態素解析APIで日本語テキストを解釈

Yahoo!の形態素解析APIで、日本語テキストを形態素解析するAppleScriptです。

Yahoo!に開発者登録(無料)して、アプリケーションIDを取得し、リスト中のretAccessKey()ハンドラにアプリケーションIDを記入すると実行可能です。

単語ごとに「品詞」「よみがな」などを取得できます。辞書が充実しているためか、自分の名前も正しく単語として認識されました。

形態素解析エンジンはローカルに置いて、辞書をカスタマイズするべきだと思っていますが、Yahoo!のAPI(が備えている辞書)だとそれなりに使える感じがします。

Yahoo!のテキスト解析系APIはひととおり試してみましたが、

 校正支援API:漢字の誤変換は指摘してくれるが、助詞の間違いなどは指摘してくれない
 キーフレーズ抽出API:使えるかどうか評価が難しい
 かな漢字変換API:呼んで使えるが、使い道が難しい

この形態素解析APIが一番実用度が高そうな感じがします。

AppleScript名:Yahoo! 形態素解析APIで日本語テキストを解釈
– Created 2016-11-25 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”

–http://developer.yahoo.co.jp/webapi/jlp/ma/v1/parse.html

–日本語形態素解析Web APIは、24時間以内で1つのアプリケーションIDにつき50000件のリクエストが上限となっています。また、1リクエストの最大サイズを100KBに制限 しています。

property dictStack : missing value – stack to hold array of dictionaries
property textInProgress : “” – string to collect text as it is found
property anError : missing value – if we get an error, store it here

set japaneseText to “私の名前は長野谷です。”

set reqURLStr to “http://jlp.yahooapis.jp/MAService/V1/parse”

set aKey to retAccessKey() of me

set aRec to {|key|:aKey, sentence:japaneseText, results:“ma”, page:“1″, output:“xml”, appid:aKey}
set aURL to retURLwithParams(reqURLStr, aRec) of me
set aRes to callRestGETAPIAndParseXMLResults(aURL) of me

set aRESCode to responseCode of aRes
if aRESCode is not equal to 200 then return false

set aRESHeader to responseHeader of aRes
set aXMLres to (xml of aRes)

set parsedList to (aXMLres’s valueForKeyPath:“ResultSet.ma_result.word_list.word.surface.contents”) as list
–>  {”私”, “の”, “名前”, “は”, “長野谷”, “です”, “。”}

set yomiganaList to (aXMLres’s valueForKeyPath:“ResultSet.ma_result.word_list.word.reading.contents”) as list
–>  {”わたし”, “の”, “なまえ”, “は”, “ながのや”, “です”, “。”}

set kindList to (aXMLres’s valueForKeyPath:“ResultSet.ma_result.word_list.word.pos.contents”) as list
–>  {”名詞”, “助詞”, “名詞”, “助詞”, “名詞”, “助動詞”, “特殊”}

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseXMLResults(aURL)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set aXmlRec to my makeRecordWithXML:resStr
  
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {xml:aXmlRec, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseXMLResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on retAccessKey()
  return “xxXxxxXxXXxxxXXXXXXXXXXXXXXxXXXxxxXXxXXxxXXxxxXXXxxXXXX-” –Yahoo! API Key
end retAccessKey

on urlencodeStr(aStr)
  set aString to current application’s NSString’s stringWithString:aStr
  
set aString to (aString’s stringByAddingPercentEncodingWithAllowedCharacters:(current application’s NSCharacterSet’s URLQueryAllowedCharacterSet())) as text
  
return aString
end urlencodeStr

——–XMLParse Lib

on makeRecordWithXML:xmlString
  set my dictStack to current application’s NSMutableArray’s array() – empty mutable array
  
set anEmpty to current application’s NSMutableDictionary’s |dictionary|()
  (
my dictStack)’s addObject:anEmpty – add empty mutable dictionary
  
set my textInProgress to current application’s NSMutableString’s |string|() – empty mutable string
  
  
set anNSString to current application’s NSString’s stringWithString:xmlString
  
set theData to anNSString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
  
set theNSXMLParser to current application’s NSXMLParser’s alloc()’s initWithData:theData
  
  
theNSXMLParser’s setDelegate:me
  
  
set theResult to theNSXMLParser’s parse()
  
if theResult then – went OK, get first item on stack
    return ((my dictStack)’s firstObject()) –as record
  else
    error (my anError’s localizedDescription() as text)
  end if
end makeRecordWithXML:

– this is an XML parser delegate method. Called when new element found
on parser:anNSXMLParser didStartElement:elementName namespaceURI:aString qualifiedName:qName attributes:aRecord
  set parentDict to my dictStack’s lastObject()
  
set childDict to current application’s NSMutableDictionary’s |dictionary|()
  
if aRecord’s |count|() > 0 then
    childDict’s setValue:aRecord forKey:“attributes”
  end if
  
  
set existingValue to parentDict’s objectForKey:elementName
  
  
if existingValue is not missing value then
    if (existingValue’s isKindOfClass:(current application’s NSMutableArray)) as boolean then
      set theArray to existingValue
    else
      set theArray to current application’s NSMutableArray’s arrayWithObject:existingValue
      
parentDict’s setObject:theArray forKey:elementName
    end if
    
    
theArray’s addObject:childDict
  else
    parentDict’s setObject:childDict forKey:elementName
  end if
  
  (
my dictStack)’s addObject:childDict
end parser:didStartElement:namespaceURI:qualifiedName:attributes:

– this is an XML parser delegate method. Called at the end of an element
on parser:anNSXMLParser didEndElement:elementName namespaceURI:aString qualifiedName:qName
  if my textInProgress’s |length|() > 0 then
    set dictInProgress to my dictStack’s lastObject()
    
dictInProgress’s setObject:textInProgress forKey:“contents”
    
set my textInProgress to current application’s NSMutableString’s |string|()
  end if
  
  
my dictStack’s removeLastObject()
end parser:didEndElement:namespaceURI:qualifiedName:

– this is an XML parser delegate method. Called when string is found. May be called repeatedly
on parser:anNSXMLParser foundCharacters:aString
  if (aString’s stringByTrimmingCharactersInSet:(current application’s NSCharacterSet’s whitespaceAndNewlineCharacterSet()))’s |length|() > 0 then
    (my textInProgress)’s appendString:aString
  end if
end parser:foundCharacters:

– this is an XML parser delegate method. Called when there’s an error
on parser:anNSXMLParser parseErrorOccurred:anNSError
  set my anError to anNSError
end parser:parseErrorOccurred:

★Click Here to Open This Script 

2016/11/21 じゃらんAPIで宿情報を検索する

リクルートの旅行情報サイト「じゃらん」の「じゃらん宿表示API アドバンス」で宿情報を検索するAppleScriptです。

実行前にあらかじめじゃらんWebサービスの開発者登録を行い、「じゃらんAPIキー」を入手してください(登録無料)。

「じゃらん宿表示API アドバンス」はRESTful API(JSONを返す)ではなく、割としっかりとした論理構造を持つXMLを返してきます。

また、リストが割と長くなっていますが、掲載にあたって普段使っているXMLLibの内容を本Script内に展開したことが原因です(ありあわせのコードのつぎはぎで、さほどコードは書いておらず、動作確認を含めてあまり時間をかけていません)。

本Scriptで指定した検索条件は、

  横浜みなとみらい地区で2016年11月21日宿泊の1室、大人2名で宿泊可能な施設

です。テスト実行にあたっては、日付情報を実行時点での未来に設定しておく必要があります。

空室の詳細な状況確認については、別途「空室検索API」を。エリアコードの検索については「エリア検索API」を、温泉の検索については、「温泉検索API」を呼び出すようになるようです。ただ、一般公開されているAPIはこれらの情報検索系のみで、実際の宿泊予約についてはWebブラウザから行うことになるようです。

AppleScript名:じゃらんAPIで宿情報を検索する
– Created 2016-11-20 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4323

–http://www.jalan.net/jw/jwp0100/jww0102.do

property dictStack : missing value – stack to hold array of dictionaries
property textInProgress : “” – string to collect text as it is found
property anError : missing value – if we get an error, store it here

set reqURLStr to “http://jws.jalan.net/APIAdvance/HotelSearch/V1/”

set aKey to retAccessKey() of me

–横浜みなとみらい地区で2016年11月21日宿泊の1室、大人2名で宿泊可能な施設
set aRec to {|key|:aKey, s_area:“140202″, stay_date:“20161121″, room_count:“1″, adult_num:“2″, sc_num:“0″}
set aURL to retURLwithParams(reqURLStr, aRec) of me
set aRes to callRestGETAPIAndParseResults(aURL) of me
set aRESCode to responseCode of aRes
if aRESCode is not equal to 200 then return false
set aRESHeader to responseHeader of aRes
set aXMLres to (xml of aRes)
set aNameRes to (aXMLres’s valueForKeyPath:“Results.Hotel.HotelName.contents”) as list
–>  {”ホテルルートイン横浜馬車道”, “エスカル横浜”, “ホテル エディット 横濱”, “ホテルパセラの森 横浜関内(2015年NEWオープン)”, “ヨコハマプラザホテル”, “横浜 マンダリンホテル”, “ホテルモントレ横浜”, “ヨコハマホステルヴィレッジ 林会館”, “フレックステイイン横浜”, “東横イン横浜スタジアム前1(旧東横イン横浜スタジアム前本館)”}

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set aXmlRec to my makeRecordWithXML:resStr
  
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {xml:aXmlRec, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on retAccessKey()
  return “xxxXXXXXXXXXxX” –じゃらんAPIキー
end retAccessKey

on urlencodeStr(aStr)
  set aString to current application’s NSString’s stringWithString:aStr
  
set aString to (aString’s stringByAddingPercentEncodingWithAllowedCharacters:(current application’s NSCharacterSet’s URLQueryAllowedCharacterSet())) as text
  
return aString
end urlencodeStr

——–XMLParse Lib

on makeRecordWithXML:xmlString
  set my dictStack to current application’s NSMutableArray’s array() – empty mutable array
  
set anEmpty to current application’s NSMutableDictionary’s |dictionary|()
  (
my dictStack)’s addObject:anEmpty – add empty mutable dictionary
  
set my textInProgress to current application’s NSMutableString’s |string|() – empty mutable string
  
  
set anNSString to current application’s NSString’s stringWithString:xmlString
  
set theData to anNSString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
  
set theNSXMLParser to current application’s NSXMLParser’s alloc()’s initWithData:theData
  
  
theNSXMLParser’s setDelegate:me
  
  
set theResult to theNSXMLParser’s parse()
  
if theResult then – went OK, get first item on stack
    return ((my dictStack)’s firstObject()) –as record
  else
    error (my anError’s localizedDescription() as text)
  end if
end makeRecordWithXML:

– this is an XML parser delegate method. Called when new element found
on parser:anNSXMLParser didStartElement:elementName namespaceURI:aString qualifiedName:qName attributes:aRecord
  set parentDict to my dictStack’s lastObject()
  
set childDict to current application’s NSMutableDictionary’s |dictionary|()
  
if aRecord’s |count|() > 0 then
    childDict’s setValue:aRecord forKey:“attributes”
  end if
  
  
set existingValue to parentDict’s objectForKey:elementName
  
  
if existingValue is not missing value then
    if (existingValue’s isKindOfClass:(current application’s NSMutableArray)) as boolean then
      set theArray to existingValue
    else
      set theArray to current application’s NSMutableArray’s arrayWithObject:existingValue
      
parentDict’s setObject:theArray forKey:elementName
    end if
    
    
theArray’s addObject:childDict
  else
    parentDict’s setObject:childDict forKey:elementName
  end if
  
  (
my dictStack)’s addObject:childDict
end parser:didStartElement:namespaceURI:qualifiedName:attributes:

– this is an XML parser delegate method. Called at the end of an element
on parser:anNSXMLParser didEndElement:elementName namespaceURI:aString qualifiedName:qName
  if my textInProgress’s |length|() > 0 then
    set dictInProgress to my dictStack’s lastObject()
    
dictInProgress’s setObject:textInProgress forKey:“contents”
    
set my textInProgress to current application’s NSMutableString’s |string|()
  end if
  
  
my dictStack’s removeLastObject()
end parser:didEndElement:namespaceURI:qualifiedName:

– this is an XML parser delegate method. Called when string is found. May be called repeatedly
on parser:anNSXMLParser foundCharacters:aString
  if (aString’s stringByTrimmingCharactersInSet:(current application’s NSCharacterSet’s whitespaceAndNewlineCharacterSet()))’s |length|() > 0 then
    (my textInProgress)’s appendString:aString
  end if
end parser:foundCharacters:

– this is an XML parser delegate method. Called when there’s an error
on parser:anNSXMLParser parseErrorOccurred:anNSError
  set my anError to anNSError
end parser:parseErrorOccurred:

★Click Here to Open This Script 

2016/11/12 国立国会図書館から書籍をタイトルで検索

国立国会図書館のオンラインサービスに問い合わせを行うAppleScriptです。

国立国会図書館の検索用APIでは、SRU/SRW/OpenSearch/OpenURL/Z39.50/OAI-PMHの6種類の呼び出し方式を用意していますが、ここでは独断と偏見でOpenSearchを用いて呼び出しています。

実行結果はXMLで返ってきますので、適宜XMLを解釈するAppleScriptのルーチンを呼び出してNSDictionaryにしたりrecord型変数にしたりしてください(タグに「:」を含む文字列が返ってくるので、NSDictionaryでないと値を取り出せない)。

xml.jpg

AppleScript名:国立国会図書館で検索
– Created 2016-11-05 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
–http://iss.ndl.go.jp/information/api/
–http://piyocast.com/as/archives/4313

set reqURLStr to “http://iss.ndl.go.jp/api/opensearch” –Protocol:OpenSearch

set aRec to {title:“Mac使いへの道”}
set aURL to retURLwithParams(reqURLStr, aRec) of me
set aRes to callOpenSearchAPIAndParseResults(aURL) of me

set aRESTcode to (responseCode of aRes) as integer
set aRESTheader to (resHeaders of aRes) as record
if (resXML of aRes) = missing value then return false
set aRESTres to (resXML of aRes) as string

–OpenSearch APIを呼ぶ
on callOpenSearchAPIAndParseResults(aURL)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {resXML:jsonString, responseCode:resCode, responseHeader:resHeaders}
end callOpenSearchAPIAndParseResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on urlencodeStr(aStr)
  set aString to current application’s NSString’s stringWithString:aStr
  
set aString to (aString’s stringByAddingPercentEncodingWithAllowedCharacters:(current application’s NSCharacterSet’s URLQueryAllowedCharacterSet())) as text
  
return aString
end urlencodeStr

★Click Here to Open This Script 

2016/11/06 XMLをrecordにv2

XMLをrecordに変換するAppleScriptです。

以前にAppleScript-Users ML上で流れていたXML→record変換のAppleScriptでしたが、動作確認を行ってもうまく動かず、そのまま放置状態になっていました。

見直してみたところ、「NSMutableDictionary’s dictionary()」というカラのmutable dictionaryを作成する部分が、うまくAppleScriptの処理系に認識されていなかったようでした。少し書き直してみました。

AppleScript名:XMLをrecordにv2
–2015 Shane Stanley & Alex Zavatone
– Modified 2016-11-06 by Takaaki Naganoya
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4306

property dictStack : missing value – stack to hold array of dictionaries
property textInProgress : “” – string to collect text as it is found
property anError : missing value – if we get an error, store it here

set xmlString to “< ?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>

Saga
Nor
én
Malm
ö
Martin
Rohde
K
øbenhavn

set xmlRes to my makeRecordWithXML:xmlString
–> {|character|:{firstName:{|contents|:”Saga”}, lastName:{|contents|:”Norén”}, city:{|contents|:”Malmö“}, partner:{firstName:{|contents|:”Martin”}, lastName:{|contents|:”Rohde”}, city:{|contents|:”København”}, attributes:{approach:”dogged”}}}}

on makeRecordWithXML:xmlString
  – set up properties
  
set my dictStack to current application’s NSMutableArray’s array() – empty mutable array
  
set anEmpty to current application’s NSMutableDictionary’s |dictionary|()
  (
my dictStack)’s addObject:anEmpty – add empty mutable dictionary
  
set my textInProgress to current application’s NSMutableString’s |string|() – empty mutable string
  
  
– convert XML from string to data
  
set anNSString to current application’s NSString’s stringWithString:xmlString
  
set theData to anNSString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
  
– initialize an XML parser with the data
  
set theNSXMLParser to current application’s NSXMLParser’s alloc()’s initWithData:theData
  
  
– set this script to be the parser’s delegate
  
theNSXMLParser’s setDelegate:me
  
  
– tell it to parse the XML
  
set theResult to theNSXMLParser’s parse()
  
if theResult then – went OK, get first item on stack
    return ((my dictStack)’s firstObject()) as record
  else – error, so return error
    error (my anError’s localizedDescription() as text)
  end if
end makeRecordWithXML:

– this is an XML parser delegate method. Called when new element found
on parser:anNSXMLParser didStartElement:elementName namespaceURI:aString qualifiedName:qName attributes:aRecord
  – store reference to last item on the stack
  
set parentDict to my dictStack’s lastObject()
  
  
– make new child
  
set childDict to current application’s NSMutableDictionary’s |dictionary|()
  
  
– if there are attributes, add them as a record with key “attributes”
  
if aRecord’s |count|() > 0 then
    childDict’s setValue:aRecord forKey:“attributes”
  end if
  
  
– see if there’s already an item for this key
  
set existingValue to parentDict’s objectForKey:elementName
  
  
if existingValue is not missing value then
    – there is, so if it’s an array, store it…
    
if (existingValue’s isKindOfClass:(current application’s NSMutableArray)) as boolean then
      set theArray to existingValue
    else
      – otherwise create an array and add it
      
set theArray to current application’s NSMutableArray’s arrayWithObject:existingValue
      
parentDict’s setObject:theArray forKey:elementName
    end if
    
    
– then add the new dictionary to the array
    
theArray’s addObject:childDict
  else
    – add new dictionary directly to the parent
    
parentDict’s setObject:childDict forKey:elementName
  end if
  
  
– also add the new dictionary to the end of the stack
  (
my dictStack)’s addObject:childDict
end parser:didStartElement:namespaceURI:qualifiedName:attributes:

– this is an XML parser delegate method. Called at the end of an element
on parser:anNSXMLParser didEndElement:elementName namespaceURI:aString qualifiedName:qName
  – if any text has been stored, add it as a record with key “contents”
  
if my textInProgress’s |length|() > 0 then
    set dictInProgress to my dictStack’s lastObject()
    
dictInProgress’s setObject:textInProgress forKey:“contents”
    
    
– reset textInProgress property for next element
    
set my textInProgress to current application’s NSMutableString’s |string|()
  end if
  
  
– remove last item from the stack
  
my dictStack’s removeLastObject()
end parser:didEndElement:namespaceURI:qualifiedName:

– this is an XML parser delegate method. Called when string is found. May be called repeatedly
on parser:anNSXMLParser foundCharacters:aString
  – only append string if it’s not solely made of space characters (which should be, but aren’t, caught by another delegate method)
  
if (aString’s stringByTrimmingCharactersInSet:(current application’s NSCharacterSet’s whitespaceAndNewlineCharacterSet()))’s |length|() > 0 then
    (my textInProgress)’s appendString:aString
  end if
end parser:foundCharacters:

– this is an XML parser delegate method. Called when there’s an error
on parser:anNSXMLParser parseErrorOccurred:anNSError
  set my anError to anNSError
end parser:parseErrorOccurred:

★Click Here to Open This Script 

2016/11/05 XMLをNSDictionaryに

オープンソースのXMLDictionary(By Nick Lockwood)をFramework化した「XmlToDictKit」を呼び出して、XMLをNSDictionaryにするAppleScriptです。

XMLをNSDictionaryにするのには、なかなか手こずらされており、依然としてSatimageのXMLLib OSAXが手放せない状況ですが、OSAXを使わずになんとかする方法についてはつねに模索しておりました。

「XML Parser」などをキーワードにGithub上でいろいろ物色していたところ、そんなに気合の入ったキーワードでなくても、「XML NSDictionary」ぐらいでいろいろ見つかりました。

それらを人気順でソートし上から順番に物色。条件が合って手軽にフレームワーク化に成功したのがこの「XMLDictionary」です。

XMLをNSDictionaryに変換し、さらにNSDictionaryをrecordまで変換すればAppleScriptで簡単に取り扱えます(NSDictionaryの属性値ラベルに空白などが入っていなければ)。

フレームワークについては、例によってOS X 10.10をターゲットにしてビルドしてみました。~/Library/Frameworksに入れてご利用ください(あくまで自己責任で)。

XMLそのものよりも、RSSのParseが割と手間だったので、RSSを手軽に扱えるようになったことのメリットが大きいと感じています。

→ FrameworkのZipアーカイブのダウンロード(30KB)

AppleScript名:XMLをDictionaryに(remote file)
– Created 2016-11-05 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “XmlToDictKit” –https://github.com/nicklockwood/XMLDictionary
–http://piyocast.com/as/archives/4304

set aURL to current application’s |NSURL|’s alloc()’s initWithString:“http://www.ibiblio.org/xml/examples/shakespeare/all_well.xml”
set xmlString to current application’s NSString’s alloc()’s initWithContentsOfURL:aURL encoding:(current application’s NSUTF8StringEncoding) |error|:(missing value)
if xmlString = missing value then return false
set xmlDoc to (current application’s NSDictionary’s dictionaryWithXMLString:xmlString) as record

★Click Here to Open This Script 

AppleScript名:XMLをDictionaryに(local file)
– Created 2016-11-05 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “XmlToDictKit” –https://github.com/nicklockwood/XMLDictionary
–http://piyocast.com/as/archives/4304

set aFile to POSIX path of (choose file)
set aURL to current application’s |NSURL|’s fileURLWithPath:aFile
set xmlString to current application’s NSString’s alloc()’s initWithContentsOfURL:aURL encoding:(current application’s NSUTF8StringEncoding) |error|:(missing value)
if xmlString = missing value then return false
set xmlDoc to (current application’s NSDictionary’s dictionaryWithXMLString:xmlString) as record

★Click Here to Open This Script 

AppleScript名:RSSをDictionaryに(remote file)
– Created 2016-11-05 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “XmlToDictKit” –https://github.com/nicklockwood/XMLDictionary
–http://piyocast.com/as/archives/4304

set aURL to current application’s |NSURL|’s alloc()’s initWithString:“http://piyocast.com/as/feed”
set xmlString to current application’s NSString’s alloc()’s initWithContentsOfURL:aURL encoding:(current application’s NSUTF8StringEncoding) |error|:(missing value)
if xmlString = missing value then return false
set xmlDoc to (current application’s NSDictionary’s dictionaryWithXMLString:xmlString) as record
set aChannel to |item| of channel of xmlDoc
set aDoc1 to first item of aChannel
–>  {category:{”Application Control”, “10.10 savvy”, “10.11 savvy”, “10.12 savvy”}, dc:creator:”maro”, comments:”http://piyocast.com/as/archives/4304#comments”, title:”XMLをNSDictionaryに”, link:”http://piyocast.com/as/archives/4304″, pubDate:”Sat, 05 Nov 2016 21:21:17 +0900″, description:”オープンソースのXMLDictionary(By Nick Lockwood)をFramework化した「XmlToDictKit」を呼び出して、XMLをNSDictionaryにするAppleScr…”, guid:{__text:”http://piyocast.com/as/archives/4304″, _isPermaLink:”false”}, wfw:commentRss:”http://piyocast.com/as/archives/4304/feed/”}

★Click Here to Open This Script 

2016/11/02 iTunesライブラリ中の楽曲のジャンルを集計して多い順にソートして出力

iTunesライブラリ中の楽曲のジャンルを集計して多い順にソートして返すAppleScriptです。

集計部分をCocoaで行っているので、集計部分自体が相当に高速なのですが、手元のMacBook Pro Retina 2012(Core i7 2.6GHz)で音声データが6,861件iTunesに登録されている環境で処理したところ、

  アプリケーション(iTunes)を直接呼び出し:3.5秒
  すべてフレームワーク経由で処理:0.03秒

と、アプリケーションを直接操作しないでフレームワーク経由で楽曲の情報を取得して処理するとアプリケーションへの問い合わせを行うよりも100倍以上高速です。

iTunes Music Library.xmlを読み取って処理してもだいたいフレームワーク経由の処理と同じかやや高速なぐらいに落ち着くと思われます。
補足:XML経由の処理はAppleScriptだと(Cocoaの機能を使っても)ムチャクチャ時間がかかりました。参考までに

ただ、処理結果が方法によってほんの微妙に変わるのはジャンル判定処理が異なるためでしょうか。やや、気になります。

GUIアプリ経由で情報を取得するのと、フレームワークを呼び出して情報を取得するのと、XMLを自力で解析するのとでは、提供してくれる機能が違うので、用途に応じて適切なものを取捨選択することになります。「現在再生中の曲」「曲リスト中で選択中のもの」といった情報が必要な場合にはGUIアプリ(iTunes)に問い合わせるのが正解です。

AppleScript名:iTunesライブラリ中の楽曲のジャンルを集計して多い順にソートして出力(アプリケーション呼び出し)
– Created 2016-10-30 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4301

tell application “iTunes”
  –set aList to genre of (every file track whose media kind is equal to song and genre is not equal to “”)
  
set aList to genre of every file track
end tell

set aRes to countItemsByItsAppearance(aList) of me
return aRes
–> {{theName:”サウンドトラック”, numberOfTimes:1721}, {theName:”ロック”, numberOfTimes:942}, {theName:”クラシック”, numberOfTimes:539},

–ジャンルのリストを出現回数で集計
on countItemsByItsAppearance(aList)
  set aSet to current application’s NSCountedSet’s alloc()’s initWithArray:aList
  
set bArray to current application’s NSMutableArray’s array()
  
set theEnumerator to aSet’s objectEnumerator()
  
  
repeat
    set aValue to theEnumerator’s nextObject()
    
if aValue is missing value then exit repeat
    
bArray’s addObject:(current application’s NSDictionary’s dictionaryWithObjects:{aValue, (aSet’s countForObject:aValue)} forKeys:{“theName”, “numberOfTimes”})
  end repeat
  
  
–出現回数(numberOfTimes)で降順ソート
  
set theDesc to current application’s NSSortDescriptor’s sortDescriptorWithKey:“numberOfTimes” ascending:false
  
bArray’s sortUsingDescriptors:{theDesc}
  
  
return bArray as list
end countItemsByItsAppearance

★Click Here to Open This Script 

AppleScript名:iTunesライブラリ中の楽曲のジャンルを集計して多い順にソートして出力(フレームワーク呼び出し)
– Created 2016-11-02 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “iTunesLibrary”
–http://piyocast.com/as/archives/4301

set library to current application’s ITLibrary’s libraryWithAPIVersion:“1.0″ |error|:(missing value)
if library is equal to missing value then return

set playLists to library’s allPlaylists()
set gArray to library’s allMediaItems()’s genre
set aRes to countItemsByItsAppearance(gArray) of me
–> {{theName:”サウンドトラック”, numberOfTimes:1722}, {theName:”ロック”, numberOfTimes:956}, {theName:”Podcast”, numberOfTimes:755},

–ジャンルのリストを出現回数で集計
on countItemsByItsAppearance(aList)
  set aSet to current application’s NSCountedSet’s alloc()’s initWithArray:aList
  
set bArray to current application’s NSMutableArray’s array()
  
set theEnumerator to aSet’s objectEnumerator()
  
  
repeat
    set aValue to theEnumerator’s nextObject()
    
if aValue is missing value then exit repeat
    
bArray’s addObject:(current application’s NSDictionary’s dictionaryWithObjects:{aValue, (aSet’s countForObject:aValue)} forKeys:{“theName”, “numberOfTimes”})
  end repeat
  
  
–出現回数(numberOfTimes)で降順ソート
  
set theDesc to current application’s NSSortDescriptor’s sortDescriptorWithKey:“numberOfTimes” ascending:false
  
bArray’s sortUsingDescriptors:{theDesc}
  
  
return bArray as list
end countItemsByItsAppearance

★Click Here to Open This Script 

2016/10/31 listのrecordをplistにserializeして、plistをde-serializeする

Listでまとめたrecordをエンコーディングしてplist文字列にして(serialize)、さらにそれを元に戻す(deserialize)AppleScriptです。

本来、「エンコーダーとデコーダーは一緒に作る」はずのものですが、plistへのエンコードScriptしかできておらず、世の中に転がっているObjective-Cのサンプルとにらめっこしてもさっぱり分からなかったので、AppleScript Users MLに質問を投げてみたら、Shane Stanleyからあっさりと、

「これ、もうdeprecatedなmethodなんで使っちゃダメだよ。あと、deserializeするときには文字列をそのまま渡さないでね」

というアドバイスとコードそのものを教えてもらいました。というわけで、整理して掲載しておきます。

AppleScript名:listのrecordをplistにserializeして、plistをde-serializeする
– Created 2016-10-30 by Takaaki Naganoya
– Modified 2016-10-31 by Shane Stanley
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4296

set aList to {{theName:“Sound Track”, numberOfTimes:1721}, {theName:“Rock”, numberOfTimes:942}}
set aRes to serializeToPlistString(aList) of me
set bRes to (deserializeToPlistString(aRes) of me) as list
–>  {{numberOfTimes:1721, theName:”Sound Track”}, {numberOfTimes:942, theName:”Rock”}}

–list or record –> XML-format plist string
on serializeToPlistString(aList as {list, record})
  set pListData to current application’s NSPropertyListSerialization’s dataWithPropertyList:aList |format|:(current application’s NSPropertyListXMLFormat_v1_0) options:0 |error|:(missing value)
  
set bStr to (current application’s NSString’s alloc()’s initWithData:pListData encoding:(current application’s NSUTF8StringEncoding)) as string
  
return bStr
end serializeToPlistString

–XML-format plist string–> list or record
on deserializeToPlistString(aStr as string)
  set deStr to current application’s NSString’s stringWithString:aStr
  
set theData to deStr’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aList to current application’s NSPropertyListSerialization’s propertyListWithData:theData options:(current application’s NSPropertyListMutableContainersAndLeaves) |format|:(missing value) |error|:(missing value)
  
return aList
end deserializeToPlistString

★Click Here to Open This Script 

2016/10/30 Listのrecordをエンコーディングしてplist文字列にする

Listでまとめたrecordをエンコーディングしてplist文字列にするAppleScriptです。

ただのrecordでもなんでもいいのですが、plistのファイルに書き込むのではなく、文字列として取得します。

AppleScriptをTerminal上で呼び出して、複数のAppleScript同士でデータをやりとりする場合に、リストやレコードをそのままやりとりすることは困難です。そこで、plistの文字列にして標準出力に出すことを検討してみました。

ご注意:本Scriptはdeprecatedなmethodを使っているため、アップデート版を利用してください。

AppleScript名:Listのrecordをエンコーディングしてplist文字列にする
– Created 2016-10-30 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4295

set aList to {{theName:“サウンドトラック”, numberOfTimes:1721}, {theName:“ロック”, numberOfTimes:942}}

–2D Arrayをplistの文字列にエンコードする
set anArray to current application’s NSArray’s arrayWithObject:aList
set pListData to current application’s NSPropertyListSerialization’s dataFromPropertyList:anArray |format|:(current application’s NSPropertyListXMLFormat_v1_0) errorDescription:(missing value)
set bStr to (current application’s NSString’s alloc()’s initWithData:pListData encoding:(current application’s NSUTF8StringEncoding)) as string

(*
–>  ”<?xml version=\”1.0\” encoding=\”UTF-8\”?>
<!DOCTYPE plist PUBLIC \”-//Apple//DTD PLIST 1.0//EN\” \”http://www.apple.com/DTDs/PropertyList-1.0.dtd\”>
<plist version=\”1.0\”>
<array>
  <array>
    <dict>
      <key>numberOfTimes</key>
      <integer>1721</integer>
      <key>theName</key>
      <string>サウンドトラック</string>
    </dict>
    <dict>
      <key>numberOfTimes</key>
      <integer>942</integer>
      <key>theName</key>
      <string>ロック</string>
    </dict>
  </array>
</array>
</plist>

*)

★Click Here to Open This Script 

2016/10/05 SDカードを検出

マウント中のドライブのマウントポイント(例:/Volumes/ドライブ名)を指定すると、当該ドライブがSDカードかどうかを判定するAppleScriptです。

system_profilerコマンドを呼び出して詳細な情報を取得して判定するようにして、手元にある2枚のSDカードはこれで判定できています。MacBook Pro Retina 2012の内蔵SDカードリーダー、およびUSB接続のSDカードリーダーの両方で判定できることを確認しています。

system_profilerの実行結果は、

mountedsd1.jpg

MacBook Pro Retina内蔵SDカードリーダーに入れたSDXCカード「JVCCAM_SD」
-> {writable:”yes”, _name:”JVCCAM_SD”, ignore_ownership:”yes”, file_system:”MS-DOS FAT32″, size_in_bytes:3.3179041792E+10, bsd_name:”disk3s1″, free_space_in_bytes:2.0819673088E+10, mount_point:”/Volumes/JVCCAM_SD”, physical_drive:{is_internal_disk:”yes”, device_name:”Built In SDXC Reader”, protocol:”Secure Digital”, media_name:”Apple SDXC Reader Media”, partition_map_type:”master_boot_record_partition_map_type”}}

USB接続SDカードリーダーに入れたSDカード「RICOHDCX」
–> {writable:”yes”, _name:”RICOHDCX”, ignore_ownership:”yes”, file_system:”MS-DOS FAT32″, volume_uuid:”3BD2F468-5B42-327C-AB96-2812A02E7126″, size_in_bytes:7.939817472E+9, bsd_name:”disk4s1″, free_space_in_bytes:5.415960576E+9, mount_point:”/Volumes/RICOHDCX”, physical_drive:{is_internal_disk:”no”, device_name:”SD Transcend”, protocol:”USB”, media_name:”TS-RDF5 SD Transcend Media”, partition_map_type:”master_boot_record_partition_map_type”}}

mountedsd2.jpg

MacBook Pro Retina内蔵SDカードリーダーに入れたSDカード「RICOHDCX」
–> {writable:”yes”, _name:”RICOHDCX”, ignore_ownership:”yes”, file_system:”MS-DOS FAT32″, volume_uuid:”3BD2F468-5B42-327C-AB96-2812A02E7126″, size_in_bytes:7.939817472E+9, bsd_name:”disk3s1″, free_space_in_bytes:5.415960576E+9, mount_point:”/Volumes/RICOHDCX”, physical_drive:{is_internal_disk:”yes”, device_name:”Built In SDXC Reader”, protocol:”Secure Digital”, media_name:”Apple SDXC Reader Media”, partition_map_type:”master_boot_record_partition_map_type”}}

USB接続SDカードリーダーに入れたSDXCカード「JVCCAM_SD」
–> {writable:”yes”, _name:”JVCCAM_SD”, ignore_ownership:”yes”, file_system:”MS-DOS FAT32″, size_in_bytes:3.3179041792E+10, bsd_name:”disk4s1″, free_space_in_bytes:2.0819673088E+10, mount_point:”/Volumes/JVCCAM_SD”, physical_drive:{is_internal_disk:”no”, device_name:”SD Transcend”, protocol:”USB”, media_name:”TS-RDF5 SD Transcend Media”, partition_map_type:”master_boot_record_partition_map_type”}}

となっており、device_nameとmedia_nameを単語ごとにリスト化し、「SD」「SDHC」「SDXC」の単語が入っているかどうかを判定しています。

同一名称のSDカードが複数枚同時にマウントされた場合の挙動については検証していないため、そういうケースには対応しきれていないと思われます。ご注意ください。

AppleScript名:SDカードを検出
– Created 2016-10-04 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4251

tell application “Finder”
  set driveList to every disk whose format is (MSDOS format) and ejectable is true and startup is false
  
  repeat with i in driveList
    set myDisk to disk of (first item of i)
    
set myMountPoint to POSIX path of (myDisk as alias)
    
–> “/Volumes/JVCCAM_SD/”
    
–> “/Volumes/RICOHDCX/”
    
set sdRes to detectSDCard(myMountPoint) of me
    
–> true –SD Card, false –Not SD Card
  end repeat
end tell

on detectSDCard(myMountPoint as string)
  
  set resData to runCommandString(“system_profiler -xml SPStorageDataType”) of me
  
set aaDict to (readPlistFromStr(resData) of me) as list
  
set aDictList to (_items of first item of aaDict)
  
  repeat with i in aDictList
    set j to contents of i
    
    set aMountPoint to (mount_point of j) as string
    
–> “/Volumes/JVCCAM_SD”
    
–> “/Volumes/RICOHDCX”
    
    if aMountPoint is not equal to “/” then
      if ((aMountPoint & “/”) is equal to myMountPoint) then
        set aDevName to words of (device_name of physical_drive of j)
        
set aMediaName to words of (media_name of physical_drive of j)
        
        –SD/SDHC/SDXCのカード検出
        
set aDevF to (“SD” is in aDevName) or (“SDHC” is in aDevName) or (“SDXC” is in aDevName)
        
set aMediaF to (“SD” is in aMediaName) or (“SDHC” is in aMediaName) or (“SDXC” is in aMediaName)
        
        if (aDevF and aMediaF) then return true
      end if
    end if
  end repeat
  
  return false
end detectSDCard

–文字列で与えたシェルコマンドを実行する
on runCommandString(commandStr as string)
  set aPipe to current application’s NSPipe’s pipe()
  
set aTask to current application’s NSTask’s alloc()’s init()
  
aTask’s setLaunchPath:“/bin/sh”
  
aTask’s setArguments:{“-c”, current application’s NSString’s stringWithFormat_(“%@”, commandStr)}
  
aTask’s setStandardOutput:aPipe
  
set aFile to aPipe’s fileHandleForReading()
  
aTask’s |launch|()
  
return current application’s NSString’s alloc()’s initWithData:(aFile’s readDataToEndOfFile()) encoding:(current application’s NSUTF8StringEncoding)
end runCommandString

–stringのplistを読み込んでRecordに
on readPlistFromStr(theString)
  set aSource to current application’s NSString’s stringWithString:theString
  
set pListData to aSource’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aPlist to current application’s NSPropertyListSerialization’s propertyListFromData:pListData mutabilityOption:(current application’s NSPropertyListImmutable) |format|:(current application’s NSPropertyListFormat) errorDescription:(missing value)
  
return aPlist
end readPlistFromStr

★Click Here to Open This Script 

2015/08/09 使用中のMacの製品呼称を取得する

使用中のMacの製品呼称を取得するAppleScriptです。

本来は、Cocoaの機能を呼び出してXMLをparseしようとしてこの数倍のプログラムを組んでいたのですが、いまひとつうまくいかずに挫折。System Eventsでparseするという安直な方法に落ち着きました(^ー^;;

→ ここの情報を参照しました

製品呼称コードは4文字パターン(例:DKQ2)と3文字パターン(例:5RU)があるようなので、シリアル番号から両方の文字列を作成して、両方試すと確実ではないでしょうか。

MacBook Air 2014で実行すると、「MacBook Air」としか返してこないことを確認、、、、いろいろ例外がありそうです。

AppleScript名:使用中のMacの製品呼称を取得する
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set aSerial to retSerial()
set aID to text ((length of aSerial) - 3) thru -1 of aSerial

set aRes to do shell script “curl -o - http://support-sp.apple.com/sp/product?cc=” & aID & “&lang=en_US”
–> “<?xml version=\”1.0\” encoding=\”utf-8\” ?><root><name>CPU Name</name><configCode>MacBook Pro (Retina, Mid 2012)</configCode><locale>en_US</locale></root>”

tell application “System Events”
  set aData to make new XML data with properties {text:aRes}
  
set aModel to value of XML element “configCode” of XML element “root” of aData
end tell
–> “MacBook Pro (Retina, Mid 2012)”

–Get Your Mac’s Hardware Serial Code
on retSerial()
  set a to (do shell script “system_profiler SPHardwareDataType | grep Serial”)
  
set b to offset of “:” in a
  
set c to (characters (b + 2) thru -1 of a) as text
  
return c
end retSerial

★Click Here to Open This Script