FileMaker 16 JSONでスクリプト引数

FileMakerのスクリプト間で複数の引数をやりとりするためには、こちらの記事で紹介したものをはじめ、今までいくつかの方法がありました。FileMaker 16で新たにJSON関数が提供されたことによって、スクリプト間連携処理が標準化され、かつ従来よりも簡単に導入できるようになりそうです。今回はSeedCodeのKC Embreyさんの、JSON関数によるスクリプト引数の処理に関する記事を紹介します。


seedcode

FileMaker 16 JSON Script Parameters

(元記事はこちら)

KC Embrey
2017/5/9

FileMaker 16でネイティブにJSONを解析する機能が導入されたことによって、ようやく複数のスクリプト引数を渡すための共通言語が提供されました。これにより、スクリプト間のやりとりが従来よりも格段に簡単になります。ただしJSONによるスクリプト引数を展開する(読む)のは比較的簡単ですが、新しい関数を使用してJSONを渡す(書く)ときにはいくつか注意しなくてはいけないことがあります。

概要:改良されたスクリプト引数

新バージョンのFileMaker 16には、Webアプリケーション間でデータをやりとりするために広く使用される軽量のデータ交換フォーマットであるJSONを読み書きするための関数が追加されました。拡張されたcURLサポートと合わせて使用して外部のWebサービスと通信することが、元々これらの関数がFileMaker 16に新たに追加された理由です。しかし、Webとのデータ通信以外でも、スクリプト引数をより信頼性高くやりとりするためにも使用できます。

(サンプルファイルをすぐに見たい方はこちらから: JSONScriptParameters.fmp12 )

私はFileMakerの使用歴が比較的短い方ですが、JSONについてはかなりの経験があります。そして正直なところ、FileMakerのJSONの実装が少し特殊だと感じました。あえて言えば、非常に重要なJSONGetElement関数の実装方法に違和感を覚えました。FileMakerのJSON実装に関して最初に、そして最も驚いたのは、以下の点です。


JSONの引数にどのようなデータ型を渡しても、FileMakerのJSONGetElement関数では結果は常にテキストとして返される。注: 16.0.2でJSONGetElementは正しく数字とブール値を返すようになり、常にテキストを返す不具合は解消されました=)

 

奇妙な挙動ですが、問題を回避することはできます。詳細は後ほど=)

ここからは、FileMakerスクリプト間で複数のスクリプト引数を渡すときに、引数の形式としてJSONを使用することが有益かどうかを検討します。現状を復習すると、スクリプト間で単一の引数を渡す機能はFileMakerに組み込まれていますが、複数の引数を渡す必要がある場合、ある引数を別の引数と区別するために何らかの文法を考える必要があります。その文法は空白の引数を理解し、引数の中に予期せぬもの(例えば改行)が含まれていた場合も対応できなくてはなりません。

現在、あるFileMakerスクリプトから別のスクリプトに複数の引数を渡すには、次の方法があります。

  • グローバル変数を設定する。ただしスクリプトが実行されるたびにクリアしなくてはいけないのが手間で、ファイル間をまたぐことができない。
  • Let()記法を使って、( name = value ; name2 = value2 ; ) のように書く。
  • 複数の引数を順番に区切り記号でつなげた一つのテキスト文字列にして渡す。
  • カスタム関数を使う。例えば、fmpstandardsの#Name-Valueなど。

スクリプト引数をJSON形式にすることははたして改善か?

(短い答え : Yes) しかし注意するべき点がいくつかあります。それらは主にJSONを(読むときよりも)書くときに関することです。これらを考慮してJSONをどのように書いたり読んだりするのがいいかを示すため、それらをサンプルファイルにまとめました。この記事を読み進めるにあたって、このファイルは必須ではありませんが、自分自身のJSONを解析するときにコピーして使用すると便利なスクリプトがファイルに含まれています。サンプルファイルはこちらからダウンロードしてください: JSONScriptParameters.fmp12

FileMaker 16でJSONを書く

まず、複数の引数を持つJSON文字列を書く方法を見てみましょう。次の式は、3つの名前と値のペア(3つのスクリプト引数)を持つJSONオブジェクトを作成します。

JSONSetElement ( "{}";
 ["Animal"; SomeTable::Animal ; JSONString];
 ["Age"; SomeTable::Age ; JSONNumber];
 ["Vaccinated"; SomeTable::Vaccinated ; JSONBoolean]
)

これは次のような文字列を作成します。

{"Age":12,"Animal":"Dog","Vaccinated":true}

スクリプト引数として使用された場合、目的の個々の引数を次のように取得できます:

JSONGetElement ( Get ( ScriptParameter ) ; "Animal" )

これは ”Dog”を返します。

このようにJSONでいろいろ試したいのであれば、データビューアを使う方が、スクリプトよりも簡単に式を変更できて便利でしょう。

FileMaker 16 JSONスクリプトのパラメータ

この簡単なサンプルに関して、いくつか注意しなくてはいけない点があります。

名前の付いた要素は、渡す順序にかかわらず、名前のアルファベット順に並べられる。

 

そして

名前は大文字と小文字が区別されるため、この例では JSONGetElement ( Get ( ScriptParameter ) ; “animal” ) は値を返さない。

 

各オブジェクトの3番目の引数でデータ型(JSONString, JSONNumberなど)を指定している点に注目してください。渡す各値の型を指定するか、3番目の引数をJSONStringなどの代わりに空(“”)にして、JSONパーサーに自動的に判断させます。現時点ではこれらの値の自動補完機能はないので、それらを覚えておく必要があります。

JSONの値の7つのデータ型はdocumentationで見ることができますが、参照用にここにあげておきます(クリックで詳細を表示)。

JSONString
 

JSONString

JSONに含まれる値は、渡されたのが数字であったとしても、常に引用符で囲まれます。これは、渡す前の値にFileMakerのGetAsText()関数を適用するのと同じです。

入力例: “text1”
JSONに追加される値: “text1”
JSONGetElementの結果 “text1”

FileMaker 16 JSONString

単純な文字列を渡す

改行が\rと\nでエンコードされますが、JSONGetElementと(残念ながら)JSONListValuesでデコードされている点に注意

 

JSONNumber
 

JSONNumber

JSONに含まれる値は引用符なしになります。これは、FileMakerのGetAsNumber()関数を渡す前の値に適用する場合と同じです。以下の3番目の例では、日付を数字に変換しています。

入力例: 12345.67
JSONに追加される値: 12345.67
JSONGetElementの結果: 12345.67 (数字のように見えますが、JSONGetElementは、2番目の例に示すように、常にテキストを返します)

JSONNumberを使用して渡される空の値は0として送信されることに注意。空の値を渡したい場合はJSONNullを使用する。

FileMaker 16 JSONNumber

単純な数字の解析

FileMaker 16 JSONNumberスクリプトのパラメータ

JSONGetElementが数字の10ではなくテキストの10を返している点に注意

JSONNumber関数

JSONNumberは日付に対してGetAsNumber()と同じ変換を実行する

FMP 16 JSONスクリプトのパラメータ

JSONNumberは空の値を0に変換する

 

JSONObject
 

JSONObject

1つのJSONオブジェクトを別のオブジェクトのメンバーとしてネストします。

入力例: “{ \”a\” : 11 }”
JSONに追加される値: “{“a”:11}”
JSONGetElementの結果: “{“a”:11}”

FileMaker 16 JSONObjectスクリプトのパラメータ

JSONオブジェクト自体が、JSONオブジェクト内の値になることができる

 

JSONArray
 

JSONArray

JSON内の1つの名前に関連付けられた区切り文字で区切られた値の集合。 このような複数の値をJSONStringを使って改行区切りテキストとして渡すという方法もありますが、JSONArrayを使用すれば、GetValue()を使わずに配列の個々のメンバーを位置を指定して取得できます。

入力例: オブジェクトの配列。オブジェクトは、7つのJSONオブジェクトのいずれかになる。数字のリスト (“[1,2,3,4,5]”)、テキストのリスト (“[\“string1\”,\”string2\”,\”string3\”,\”string4\”]”)、あるいはネストされたJSON文字列のリストも可能。
JSONに追加される値: “[1,2,3,4,5]”
JSONGetElementの結果: オブジェクトの配列を含むテキスト値であり、そのオブジェクトはさらにJSONGetElementを使って抽出する必要がある。

使用例: まず、引数から配列をローカル変数に設定する。

変数を設定[ $array : JSONGetElement ( Get(ScriptParameter) ; “array”] ]

次に、配列から最初の値を取得し、別のローカル変数として設定する。

変数を設定 [ $array1 : JSONGetElement ( $array ; 0)]
FileMakerのJSON配列とオブジェクトを使った実装では、最初の項目は1ではなく、インデックス0にあることに注意。したがって、最初の項目は項目0、続いて1,2,3などとなる。これは、GetValueなどの関数では配列の最初の項目が1であるため、FileMakerユーザーにとって混乱を招く可能性がある。

FileMaker 16 JSONArrayスクリプトのパラメータ

JSONGetElementをネストして配列の2の位置(3番目)の位置を指定する

配列の2の位置(3番目)の値を直接取得する

 

JSONBoolean
 

JSONBoolean

True、true、”true”、1、および1を含んでいるテキストは、すべてtrueと等価です。False、”False”、”True”、0はfalseと等価です。実際には、5つのtrueのオプションと一致しないもの(例えば”someText”など)はすべてfalseと評価されます。

入力例: True
JSONに追加される値: true (引用符がないことに注意)
JSONGetElementの結果: 1
興味深いことに、ブール値をどのように入力しても、返される値は常に1または0になる。引数”Vaccinated”を”true”として渡し、次のif文を使用してそれを読むと、

JSONGetElement ( Get ( ScriptParameter) ; "Vaccinated" ) = "true"

値は実際には1であり”true”ではないため、falseと評価されます。

単純なブール値

FM 16 JSONBoolean

引用符付きの “True”は大文字と小文字を区別し、この場合はfalseとなる

FileMaker 16 JSONブール値

この場合はtrueとなる

 

JSONNull
 

JSONNull

これは、渡す内容に関係なく、名前付きの空のメンバーを作成します。

入力例: “text1”
JSONに追加される値: “”
JSONGetElementの結果: “”

ここでは何も表示されない

 

JSONRaw
 

JSONRaw

3番目の引数を空白(“”)にしておくことと同じです。渡された値に対して、JSONパーサがデータ型を推測します。これは、テキストと数字ではかなりうまく機能し、空白のフィールドについては空白を返します。配列も、配列のように見える場合は、配列のように扱います。しかし、下の3,4,5番目の例で示すように、テキストと数字を混在させると奇妙な判断をするようです。

入力例: “text1”
JSONに追加される値: “text1”
JSONGetElementの結果: “text1”

JSONRawは、テキストと数字が混在した値について奇妙な推定をすることがある。
 

FileMaker 16 JSON Null

JSONRawはこれをテキストとして扱う

JSONRawは括弧の開始および終了を探し、これを配列として扱う

日付を理解していない

数字とテキストが混在している場合は、最初の空白以外の文字を見て判断しているらしい

これも配列と判断され、残りは破棄される

 

ご覧のとおり、JSONに慣れていない人は、この構造を記述するのが少し難しく、この時点でJSONを使うことをあきらめるかもしれません。

JSONの作成を支援するFileMakerカスタム関数を作成することで、この作業を簡単にすることができます。カスタム関数は一般的な作業を簡単にすることができ、ここでは”JSONSetNumber”のように名前に入れることで、3番目の引数を覚えておく必要がなくなります。私は3番目の引数を覚えやすいと思っていますが、JSONの記述方法にかかわらず、JSONで引数を渡すことの最大の利点は、どんな方法で生成しても、受ける側のスクリプトに渡される文字列は常に標準化された構造になるという点です。

JSONを読む – スクリプト引数を展開する

ひとたびJSONの配列を引数にしてスクリプトに渡したら、ここからが利点を享受できるところです。
まず、受信した引数が有効なJSON形式であることをスクリプトで確認します。

FileMaker 16 JSON解析の例

次に、JSON文字列から値を取得します。ここでいくつかの選択肢があります:

1. 配列の各値を、ループまたはカスタム関数を使用してローカル変数に分解する

2. ローカル変数に必要なそれぞれの引数を、スクリプトの最初のJSONGetElement行で設定する

3. スクリプト引数から、必要な引数のみを、それぞれ使用するときに抽出する

おそらくもっとも便利な2の方法では、次の式を使って、入力からローカル変数へ個別に引数を設定できます。

変数を設定[$sc_Animal; JSONGetElement ( Get(スクリプト引数); "Animal")]

これをスクリプトに必要な引数の数だけ繰り返します。この方法の利点は、別の開発者がスクリプトの最初を読むだけで、どの変数を使用しているかを知ることができるという点です。以下に、サンプルファイルの該当するスクリプトを示します。

FileMaker 16 JSONスクリプトのパラメータの例

19-24行目で、このスクリプトに渡す引数にどの名前を使用すべきかを明確にしていることに注目してください。引数の名前が与えられて、スクリプトがJSONを期待していることがわかれば、このスクリプトに引数を渡すために必要なものはすべて揃っています。これは大きな利点です。引数を受け取る方法を見るためにスクリプトを調べる必要はもうありません。

私は、上の例のように必要なスクリプト引数を1行ごとに書く方法が気に入っています。ただし、要素をループしながらみつかったキーごとに変数を設定するような場合は、JSONListValuesの注意点について考慮する必要があります。

JSONListValuesでの注意点 – 改行文字

たとえば、複数の値を含むドロップダウンリストフィールドの値を渡したとします。この結果は、次のようなJSON形式の文字列になります。

{ "Animal" : "Cat\rBird\rIguana" }

文字列中の\rは改行文字を表します。JSONListKeysを使用すると、キー/名前としてAnimalが1つ返されます。

一方、JSONListValuesメソッドを使用すると、これらの改行文字(\r)によって行が追加されるため、値の数は3となります。

Cat
Bird
Iguana

つまり、キー/名前よりも値の数が多いため、キー/名前のインデックスと値のインデックスを一致させることはできません。

これに対応するため、JSONListValuesを使用する前に、ありえる2つの改行文字[ \n (char 10) と \r (char 13) ]を置き換えて、各変数をループするときに再度戻します。これらの値を任意の文字列で置き換えることができますが、UUIDを使用する方が安全で簡単です。その文字列が既存の文字列と一致する可能性は非常に低くなります。

この実際の方法がデモファイルの以下の部分です(14〜17行目を参照)。

JSONスクリプトのパラメータエスケープ戻り値

その後、値を取得するときに、次のように元の文字に置き換えます。

Substitute( Substitute ( GetValue ( $values ; $i ) ; $char13replacement ; Char(13) ) ; $char10replacement ; Char(10) )

これはデモファイルの中のスクリプト”Parse JSON Parameters – Return Character Substitution”で確認できます。

このケースでは、FileMakerが値にエスケープ文字を残しておいて後で処理をしたほうが、より多くのメリットが得られます。ほとんどの開発者は、JSONGetKeysとJSONGetValuesの両方が常に同じ数の結果を返すことを期待していると思います。この方法は、そのままでは、おそらく開発者の頭痛の種になりそうです。

数字と日付のベストプラクティス (GetAs…関数で囲む)

JSONGetElement関数は常にテキストを返すので、JSONのスクリプト引数から日付と数字を取得するときは、明示的にデータ型を指定したほうがいいでしょう。

数字

JSONGetElementを使用するときに覚えておくべき最も重要なことは、結果が常にテキスト値になるということです。次のJSONを作成したとします。

$n = JSONSetElement ( "{}" ; "num" ; 10 ; JSONNumber );

値の型にJSONNumberを指定しても、JSONGetElementはテキストを返します。

つまり次の式はfalseと評価されます。

JSONGetElement ( $n ; "num" ) > 2

これを正しく処理するには、GetAsNumber()関数を使用して結果を数字として返すようにFileMakerに指示する必要があります。これによって次の式はtrueと評価されます。

GetAsNumber( JSONGetElement ( $n ; "num" ) ) > 2

日付

JSONの場合に限らず、日付をテキストとして受け渡すことは、FileMakerでは非常に難しいです。ロケールによって異なる日付形式が使用され、FileMakerによって日付がテキストに変換されるときには、ファイルが作成された時点での日付形式が使用されます。つまり、日付「11/12/2016」をテキストとして渡すことは、米国のロケールのユーザにはうまくいくかもしれませんが、別の国で作成されたファイルにスクリプトが貼り付けられると、テキストは「12/11/2016」となる可能性があります。

このため、日付を引数として渡す最善の方法は、まず数字に変換することです。その例を以下に示します。

JSONSetElement ( "{}" ; "date" ; ExampleField::Date ; JSONNumber)

JSONGetElementを使用した場合の結果は常にテキストとして返されるため、GetAsDate関数を使用する前に引数を再び数字に変換する必要があります。

フィールド設定[ Table::Field ; 値: GetAsDate ( GetAsNumber ( JSONGetElement ( $n ; "date" ) ) ) ]

結論

これらの点をすべて考慮して、JSONはFileMakerのスクリプト間で変数を渡して展開するのに効果的な方法だと私は考えているでしょうか?

もちろんです! 完璧ではないとしても、FileMakerでの開発においてこれが新しい標準になることはすばらしいことです。

私がこれを新しい標準として見ている最大の理由は、JSON文字列がどのような方法で生成されても、受け取る側のスクリプトでは標準のJSON形式になるからです。これにより、デバッグ、修正、既存のスクリプトへの追加が非常に簡単になります。

FileMakerのJSONの実装は、もともとは外部APIとの統合を目的としていましたが、FileMakerスクリプト間で複数のスクリプト引数を渡すための新しい標準に十分になりえます。

利点

  • JSON文字列の引数がどのような方法で生成されていても、受け取る側のスクリプトでは常に標準のJSON形式になります。
  • JSONは値の内容に影響を受けることがありません。改行を使用して引数を区切っていたときのように、引数の中に改行が入ってしまうことについて心配する必要はありません。
  • JSONはJSONオブジェクト自体を引数として扱うこともできます。そのため、すべての引数を別のスクリプトに渡す必要がある場合(例えばPSOSを使用するときなど)、1つの新規のスクリプト引数として、そのまま含めることができます。

短所と不可解な点

  • JSONGetElementは常にテキスト値を返します。数字の値を渡してJSONNumber型を指定していても、値として数字を取得するためにはGetAsNumber()関数を使用しなければならない点について、手間に感じるかもしれません。
  • JSON関数は、ランタイムソリューションではサポートされません。
  • JSONSetElement関数は、[計算式の指定]ダイアログボックスを使わずに入力するのは少し難しいかもしれません。
  • データ型を指定するか、FileMakerに判断させる必要があります。自動的に判断させる場合、テキストと数字が混在しているときに予想外の結果になる場合があります。
  • 配列の最初の項目は、ほとんどのFileMaker関数とは違って(1ではなく)0であることを覚えておく必要があります。
  • 空のフィールドをJSONNumberとして渡すと、JSONGetElementの値は空ではなく0になります。

このブログは実際には共同作業の成果です。素晴らしい知見とフィードバックを提供してくれたJason YoungとTodd Geistに感謝します。

Leave a Reply