レコードをテキストに変換する方法の一歩先
MacBook・iPhone・iPadのSafariにおいて、あらゆるページで自動ログイン・自動入力を目指した上記 auto_login*1スクリプトの作成では、その過程で様々な問題が持ち上がった...。
- 今回のスクリプト作成で最後まで悩んでいたのは、AppleScriptのレコードを文字列に変換する方法だった。
- ログイン情報をJavaScriptで利用するにはJSON形式に変換する必要があり、どうしても必要な処理だったのだ。
- ところが、AppleScriptにはレコードを文字列にする方法が提供されていない。(as textしてもエラーになる)
- AppleScriptエディタでレコードを結果として表示した時は文字列になっているのだけど、
- それを毎回いちいちコピーして、あそこにペーストしてください、というダサイ仕様には絶対にしたくないと思っていた。
- にもかかわらず、レコード文字列変換の方法が全く思い付かなかった。
- 実は最初にauto_loginスクリプトを作り始めた時から、レコード文字列変換はそのうち必要になると思っていた。
- かれこれ一週間以上、心の片隅で常に悩み続けていたのだけど、一向に閃かない...。
- GUIスクリプトでコピー&ペーストするか、いやクリップボードの履歴にパスワードが残ってしまうのは問題あり。
- いっそのこと、レコード使うのやめて、JSONなテキストで管理しようかなんて思い始めていた。
- そんな悶々とした日々を過ごしていて、ある晩、夜中に突然目が冴えたので「もう一度検索してみるか」と思って検索してみた。すると...
- それは、過去何度も検索していたはずなのに、見落としていたのか、検索ワードが悪かったのか、出会えていなかった。
- AS Hole(AppleScriptの穴) By Piyomaru Software » エラートラップを使って、レコードをstringに変換する » Blog Archive
- まさに求めていた情報で、一気の脳が活性化していくのを感じた!気分がハイになる。
- 夜中の3時頃だったけど、そこから一気に朝まで作業して動くものを仕上げてしまった。
レコードをテキストに変換する方法
{a:1, b:2} as text
- 上記を実行するとエラーが発生して、メッセージが表示される。
- error "{a:1, b:2} のタイプを text に変換できません。" number -1700 from {a:1, b:2} to text
- ところで、エラーはtryで補足できる。
try
{a:1, b:2} as text
on error msg number num
msg & return & num
end try
- それぞれの変数には、以下の内容が代入される。
- msg変数=”{a:1, b:2} のタイプを text に変換できません。”
- num変数=−1700
- あれれ?msg変数にはレコードを文字列で表現したものが含まれている!
- ならば余分な「 のタイプを text に変換できません。」の部分を取り除けばレコードを表現するテキストになるのだ。
on text_from(a_record) try
a_record as text
on error msg
msg's items 1 thru -22 as text
end try
end text_from
text_from({a:1, b:1}) --実行結果:"{a:1, b:1}"
感謝!
素晴らしい!AppleScriptに備わっていない新たな機能、レコード → テキスト変換がこんなに簡単にできてしまった!
- 例外処理を上手く活用するこの方法は、自分の発想には全くなかった...。
- AppleScriptの穴 Piyomaru Softwareさんの素晴らしい発想に感服。
- AppleScriptの穴は、AppleScriptについて検索すると、有益な情報として、どこかで必ず絡んでくる素晴らしいサイト。
- AppleScriptの言語やライブラリとして不足している部分を、独自の発想で、いつも大胆に解決してしまう。
- そんな情報が豊富なサンプルコードとともに多数紹介されている。
- サンプルコードがちゃんと動くので、その仕組みも理解しやすい。
- 自分がAppleScriptでコーディングする時は、いつも、どこかで必ずお世話になる。
- 今回のように、ピンポイントな解決法が紹介されていたり、
- サンプルコードから新たな発想を思い付いたり、
- より効率的なアルゴリズムを見つけたり、等々。
深く深ーく、感謝です!
一般化を目指す
- ところで、上記で作ったtext_from()ハンドラに、レコード以外の引数を渡したらどうなるだろうか?
on text_from(list_or_record)
try
list_or_record as text
on error msg
msg's items 1 thru -22 as text
end try
end text_from
--サンプルコード -- 実行結果
text_from({a:1, b:2}) --"{a:1, b:2}"
text_from({{a:1}, {b:2}}) -- ""
text_from({1, 2, 3}) -- "123"
text_from("ABC") -- "ABC"
text_from("123") -- "123"
text_from(123) -- "123"
- 残念ながら、2番目、3番目のリストについては、あまり役立たない形式のテキストに変換されてしまう...。
- では、「as text」の部分を「as number」に変更し、「1 thru -22」の部分を「1 thru -24」に変更してみる。
on text_from(list_or_record)
try
list_or_record as number
on error msg
msg's items 1 thru -24 as text
end try
end text_from
--サンプルコード -- 実行結果
text_from({a:1, b:2}) --"{a:1, b:2}"
text_from({{a:1}, {b:2}}) -- "{{a:1}, {b:2}}"
text_from({1, 2, 3}) -- "{1, 2, 3}"
text_from("ABC") -- "\"ABC\""
text_from("123") -- 123
text_from(123) -- 123
- すると、ほぼ理想的な形式のテキストとして、それぞれの値が返って来た!
- さらに、ここで text_from() ハンドラが返すべき値を、再定義してみると...
-
- レコード:{a:1, b:2} → テキスト:"{a:1, b:2}"
- リスト&レコード:{{a:1}, {b:2}} → テキスト:"{{a:1}, {b:2}}"
- リスト:{1, 2, 3} → テキスト:"{1, 2, 3}"
- 以上の変換は理想だと考える。
- その特徴は、ダブルクォート内側がレコード・リストのソースコードになっていること。
- そのルールを適用するなら、テキスト・数値についてもダブルクォート内側をそのソースコードとすると一貫性を保てる。
- それを実現するため、正攻法でifを使って以下のようにしてみた。
on coding(obj) try
obj as number
if obj's class = text then
"\"" & obj & "\""
else
obj as text
end if
on error msg
msg's items 1 thru -24 as text
end try
end coding
--サンプルコード -- 実行結果
coding({a:1, b:2}) --"{a:1, b:2}"
coding({{a:1}, {b:2}}) -- "{{a:1}, {b:2}}"
coding({1, 2, 3}) -- "{1, 2, 3}"
coding("ABC") -- "\"ABC\""
coding("123") -- "\"123\""
coding(123) -- "123"
- もはや、これはレコード・リストにとどまらず、あらゆるオブジェクトをソースコード化するハンドラとなった。
coding(obj)ハンドラと改名して、今ここに出来上がる!
- 上記サンプルコードでは変数に代入してないので分かりにくいかも。
- 以下のように、変数に代入されたオブジェクトでも変換できるのだ。
on coding(obj)
try
obj as number
if obj's class = text then
"\"" & obj & "\""
else
obj as text
end if
on error msg
msg's items 1 thru -24 as text
end try
end coding
set login_info to {https___direct_jp_bank_japanpost_jp_tp1web_U010101SCK_do:{{checked:false, value:"1234", type:"text", |name|:"okyakusamaBangou1", |index|:"3"}, {checked:false, value:"5678", type:"text", |name|:"okyakusamaBangou2", |index|:"4"}, {checked:false, value:"12345", type:"text", |name|:"okyakusamaBangou3", |index|:"5"}}}
coding(login_info)
--結果:"{https___direct_jp_bank_japanpost_jp_tp1web_U010101SCK_do:{{checked:false, value:\"1234\", type:\"text\", |name|:\"okyakusamaBangou1\", |index|:\"3\"}, {checked:false, value:\"5678\", type:\"text\", |name|:\"okyakusamaBangou2\", |index|:\"4\"}, {checked:false, value:\"12345\", type:\"text\", |name|:\"okyakusamaBangou3\", |index|:\"5\"}}}"
言語環境に依存しないバージョン
- 正規表現を利用して取り出し、日本語環境以外でも利用できるようにしてみた。(英語環境でテスト)
- シェルの262,144バイト制限を考慮して、日本語環境でのみ救われるように対応してみた。
on coding(obj)
try
if obj's class = text then
"\"" & obj & "\""
else if obj's class = list or obj's class = record then
obj as number
else
obj as text
end if
on error msg
try
"require \"jcode\";$KCODE=\"u\";/\\{.*\\}/=~" & quoted form of msg & ";puts($&);" --rubyコード
do shell script "ruby -e " & quoted form of result --シェルコード
on error
--do shell scriptの262,144バイト制限エラーでも、日本語環境なら救われる
--http://developer.apple.com/jp/technotes/tn2002/tn2065.html
msg's items 1 thru -24 as text --日本語環境以外ではNG
end try
end try
end coding
--サンプルコード -- 実行結果
""
result & return & coding({a:1, b:2}) --"{a:1, b:2}"
result & return & coding({{a:1}, {b:2}}) -- "{{a:1}, {b:2}}"
result & return & coding({1, 2, 3}) -- "{1, 2, 3}"
result & return & coding("ABC") -- "\"ABC\""
result & return & coding("123") -- "\"123\""
result & return & coding(123) -- "123"
result & return & coding(true) -- "true"
result & return