モチベーション
テストデータやクライアントサイドだけで持つJSONの生成はどうしていますか?自分は地道に手打ちして、JSONパーサーサイトでチェックして...という作業を行っていましたが、かなり面倒に感じました。また、仕様が変わりプロパティ(キー)を追加した時に全てのオブジェクトにそのプロパティを追加しなくてはならないという点でも良い方法とは言えません。そこで、Apple製表計算ソフトnumbersとAppleScriptを利用し、それらの問題の解決を試みました。
表から値を取得する
以下のような表を想定します。1行目にkeyとなる文字列を、その行にvalueが並んでいます。
この表から以下のようなJSONファイルを生成するのがゴールとなります。
[
{"title": "そら豆のパスタ", "foods": ["スパゲッティ", "そら豆", "パルミジャーノ", "オリーブ油", "バター"], "bookmark": true, "tags": ["パスタ", "春"], "id": 1},
{"title": "キャベツのパスタ", "foods": ["スパゲティ", "キャベツ", "アンチョビ", "にんにく", "赤唐辛子", "オリーブ油"], "bookmark": false, "tags": ["パスタ"], "id": 2}
]
numbersファイルから辞書型を作るAppleScriptを用意する
valueが配列である場合には列がなくなるまで要素を取得し続ける必要があります。valueが要素数が一つの配列である場合と、配列の型でない場合の区別ができないため、valueの型に配列を使いたい場合には列を指定して特別な処理をする必要があります。
tell application "Numbers"
set jsonDictArray to {}
open filePathOfNumbersFile
-- アクティブなシートを取得
set activeSheet to active sheet of document 1
tell activeSheet
set theTables to every table
set myListStr to "[" & return
repeat with theTable in theTables
tell theTable
set keyRecipe to value of cell 1 of row 1 of theTable
set keyFoods to value of cell 2 of row 1 of theTable
set keyBookmark to value of cell 3 of row 1 of theTable
set keyTag to value of cell 4 of row 1 of theTable
set keyID to value of cell 5 of row 1 of theTable
set valueRecipe to value of cell 1 of row 2 of theTable
set valueFoods to {}
set rowIndex to 2 -- 開始行
set columnNum to 2 -- 列番号
repeat
try
set valueOfCell to value of cell columnNum of row rowIndex of theTable
if valueOfCell is not missing value then
set end of valueFoods to valueOfCell
set rowIndex to rowIndex + 1
else
exit repeat
end if
on error
exit repeat
end try
end repeat
-- ブックマークを取得
set valueBookmark to value of cell 3 of row 2 of theTable
-- タグを取得
set valueTag to {}
set rowIndex to 2 -- 開始行
set columnNum to 4 -- 列番号
repeat
try
set valueOfCell to value of cell columnNum of row rowIndex of theTable
if valueOfCell is not missing value then
set end of valueTag to valueOfCell
set rowIndex to rowIndex + 1
else
exit repeat
end if
on error
exit repeat
end try
end repeat
set valueID to value of cell 5 of row 2 of theTable
set jsonDict to my createDictWith({{keyRecipe, valueRecipe}, {keyFoods, valueFoods}, {keyBookmark, valueBookmark}, {keyTag, valueTag}, {keyID, valueID}})
set encoded to my encode(jsonDict)
set myListStrLength to length of myListStr
if myListStrLength = 2 then
set myListStr to myListStr & encoded
else
set myListStr to myListStr & "," & return & encoded & return
end if
end tell
end repeat
set myListStr to myListStr & "]"
end tell
end tell
取得した値を利用して辞書型に変換します。"
をエスケープする必要があるのがポイントです。
辞書型を作るAppleScript
on encode(value)
set type to class of value
if type = real then
return (value as integer) as text
else if type = boolean then
return value as text
else if type = text then
return encodeString(value)
else if type = list then
return encodeList(value)
else if type = script then
return value's toJson()
else
error "Unknown type" & type
end if
end encode
on encodeList(value_list)
set out_list to {}
repeat with value in value_list
copy encode(value) to end of out_list
end repeat
return "[" & join(out_list, ", ") & "]"
end encodeList
on encodeString(value)
return "\"" & value & "\""
end encodeString
on join(value_list, delimiter)
set original_delimiter to AppleScript's text item delimiters
set AppleScript's text item delimiters to delimiter
set rv to value_list as text
set AppleScript's text item delimiters to original_delimiter
return rv
end join
on createDictWith(item_pairs)
set item_list to {}
script Dict
on setkv(key, value)
copy {key, value} to end of item_list
end setkv
on toJson()
set item_strings to {}
repeat with kv in item_list
set key_str to encodeString(item 1 of kv)
set value_str to encode(item 2 of kv)
copy key_str & ": " & value_str to end of item_strings
end repeat
return "{" & join(item_strings, ", ") & "}"
end toJson
end script
repeat with pair in item_pairs
Dict's setkv(item 1 of pair, item 2 of pair)
end repeat
return Dict
end createDictWith
返された文字列をjsonファイルに書き出す
JSONが文字列として生成できたので、それを書き出します。as «class utf8»
を使ってキャストしないと文字化けするので注意です。
tell application "Finder"
set aTextFile to open for access (書き出したいファイルへのパス & "ファイル名.json") with write permission
set aEOF to get eof of aTextFile
try
write myListStr starting at 0 to aTextFile as «class utf8»
on error aErrorText
display dialog aErrorText
end try
close access aTextFile
end tell
終わりに
Object型に対応できていないのが心残りではありますが、抱えていた課題は解決できました。
AppleScriptはXcodeのテンプレートから削除されていたりと下火のようですが、便利なので使っていきたいです。
参考
JSONを作るAppleScriptを書くにあたり以下のリポジトリが大変参考になりました
https://github.com/mgax/applescript-json