新しいVer.16の登場により、FileMakerがサーバとクライアントの両方でJSONの入出力に対応しました。
新版のリリースと同時に多くの記事が公開されましたが、今回はSoliant ConsultingのAnders Monsenさんによる、JSONGetElement関数の解説記事をご紹介します。
Parsing JSON in FileMaker 16
(元記事はこちら)
Anders Monsen
2017/5/9
JSON (JavaScript Object Notation) は、名前と値のペアを持つオブジェクトで構成される、データを送信するためのオープンな標準フォーマットです。JSONはJavaScriptから派生していますが、他の多くのプログラミング言語がXMLの代わりに採用しています。というのも、JSONはXMLよりもはるかに構造がシンプルだからです。多くのWebサービスは、JSONかXMLのいずれか、あるいは両方で、データを返します。FileMakerの「URLから挿入」スクリプトは、Webサービスに接続してXMLやJSONなどのデータを取得します。 FileMaker 16の前までは、JSONを解析するために、テキスト処理関数を駆使してデータを抽出する必要がありました。
FileMaker 16でJSONからデータを作成および抽出する専用の関数が導入されたため、Webサービスとのやりとりがより簡単で効率的にできるようになりました。この記事ではJSONから値を抽出する方法を中心に説明します。
JSONの構造
JSONオブジェクトは、最初と最後が波括弧( { } )で、キーと値のペアの複数のセットを含みます。各キーの後にはコロン(:)が続き、キーと値の各ペアはコンマで区切られます。
{ key1 : value1 , key2 : value2 }
配列は値の集合です。配列の最初と最後は角括弧( [ ] )です。配列内の値はコンマで区切られています。配列内の値は、0から始まる数値でインデックス付けされます。以下の例の配列には、0,1,2のインデックスがあります。
[ value1 , value2 , value3 ]
データ型
値の種類は、文字列・数値・ブール値(true/false)・null・オブジェクト・配列があります。つまりJSONオブジェクトは、配列やオブジェクトとして多層にネストされたデータを格納することができ、キーへのパス(経路)をたどることで、そのキーに関連づけられた特定の値を取得することができます。
空白
JSONはキーと値のペアの間にある空白を無視します。つまり、タブ・スペース・改行を挿入して、JSONを人が読みやすくすることができます。
JSONの大きなブロックは、配列・オブジェクト・パスが識別しにくく、読みにくくなりがちです。ここで示している例では、インデントとスペースを使ってJSONを読みやすく表示しています。
JSONからデータを抽出するためには、パスとキーを知る必要があります。また抽出したい要素への経路を見るには構造を理解することが重要です。JSONをあらかじめ見ずに、JSONをループし、キーをリストとして抽出して、そのキーに基づいて値を抽出するスクリプトを作成することは可能ではありますが、構造を理解することなくデータを信頼することはできないと確信しています。Webサービスは、単純なものから複雑なものまで、興味深い方法でJSONを構築します。
そのために、 JSONFormatElements( JSON )という関数はJSONコードを再構成して読みやすくします。注:この関数は、対象としているJSONデータが有効(valid)であることを確認する方法としても使えます。無効なJSONは、”?”をエラーに関する情報とともに返します。たとえば、[]がペアになっていないオブジェクトは、以下を返します。
? * Line 1, Column 186 Missing ',' or ']' in array declaration
{}がペアになっていない場合はエラーは以下のようになります。
? * Line 1, Column 185 Missing ',' or '}' in object declaration
FileMaker以外でも、同じ機能を提供するWebサイトがいくつかあります。
- http://www.jsoneditoronline.org JSONオブジェクトをペーストして構造を見ることができます
- http://jsonlint.com ではJSONを検証できます
FileMaker 16の関数
JSONからデータを抽出するために使用されるFileMaker 16の関数は、JSONGetElementです。
JSONGetElement ( json ; keyOrIndexOrPath )
必要な引数は2つあります。1つ目はJSON自体で、通常は変数またはフィールドを指定します。2つ目はより興味深いもので、Key, Index, Pathの3つのうちのいずれかです。どれを指定するかは、要素のタイプ(文字列・配列・数値・オブジェクトなど)によって異なります。
例
実際のJSONコードを見てみましょう。以下は、コンタクト情報(名前・住所・いくつかの電話番号)を保持するJSONオブジェクトです。重要:JSONGetElementは大文字と小文字を区別するので、firstNameはfirstnameやFirstNameとは別のものとして扱われます。
{ "firstName": "John", "lastName" : "doe", "age" : 26, "address" : { "streetAddress": "123 Main street", "city" : "Anytown", "postalCode" : "12345" }, "phoneNumbers": [ { "type" : "iPhone", "number": "123-456-8888" }, { "type" : "home", "number": "123-557-8910" } ] }
このJSONオブジェクトには、オブジェクト・配列・キーと値のペアが混在しています。たとえば、 “firstName”はキーであり、 “John”は対応する値です。”address”キーの値は{}で示され、streetAddress, city, postalCodeの3つのキーと値のペアを含むオブジェクトです。”phoneNumbers”キーの値は、[]内に含まれる2つのキーと値のペアを含む配列で、”type”と”number”がキーです。
特定の要素を抽出するために、2つ目の引数(keyOrIndexOrPath)は、その要素がメインのJSONオブジェクト内のどこに存在するかに基づいて、見た目や振る舞いが変わります。
“firstName”の値を抽出する関数はシンプルです:
JSONGetElement( JSON ; "firstName" )
これは値として “John”を返します。
しかし、全く同じ関数を”streetAddress”を引数として使用すると、空の値が返されます。これは、このキーが “address”オブジェクト内にネストされているからです。これによりたどるパスが増えます。”address”オブジェクト内の値を抽出するためには、これをパスに追加する必要があります。パスの各枝はピリオドで区切られます。ルートパスを宣言する必要はありませんが、”.firstName”のようにキーの前にピリオドを置くことも可能です。
したがって”streetAddress”を取得するには、このキーの前に “address”とピリオドを追加します。
JSONGetElement( JSON ; "address.streetAddress" )
もし”address”オブジェクト内に他のオブジェクトがネストされている場合、パスはオブジェクト名をつなげて”separated.with.periods.etc”というような形式で構成されます。ここで、文字列の最後の部分は抽出される要素のキーを示します。
配列の場合は、また対応が変わります。配列は[]で囲まれています。キーはなく、代わりにJSONGetElementの中で数値インデックスを使用する必要があります。
まずは配列自体を抽出してみましょう。
JSONGetElement( JSON ; “phoneNumbers” )は配列全体を返し、上記の[ value1, value2, value3 ]の単純な例とは異なり、JSONオブジェクトが配列内にあることがわかります。
[ { "number" : "123-456-8888", "type" : "iPhone" }, { "number" : "123-557-8910", "type" : "home" } ]
キーと値のペアの2つのグループがあるので、1つ目だけを抽出するには配列のインデックスを使用する必要があります(前述のとおり引数は、キー・ インデックス ・パスのいずれか)。JSON配列はゼロスタートなので、最初の配列のインデックスは0です。
JSONGetElement( JSON ; “phoneNumbers[0]” )は以下を返します。
{"number":"123-456-8888","type":"iPhone"}
これで名前と値のペアのセットが取得できました。numberを取得するには、パスにキーを追加します。
JSONGetElement( JSON ; "phoneNumbers[0].number" )
この結果は123-456-8888になります。2つ目のnumberを取得するには、パスで[0]の代わりに[1]を使用します。
人ごとのJSONオブジェクトには、それぞれ不定の数の電話番号が含まれています。phoneNumbersの配列から単純にnumberの値のリストを抽出する方法はありません。代わりに、スクリプトや再帰カスタム関数を使用してデータをループする必要があります。
配列内の値の数を数えるために、JSONListKeysとJSONListValuesの2つの関数があります。
JSONListKeys( JSON ; “phoneNumbers” )は0から始まるキーを返します。上記の例の場合、 “0¶1″というリターン区切りのリストになります。他のFileMakerリスト関数とは異なり、末尾の改行は結果に追加されません。
JSONListValues( JSON ; “phoneNumbers” )は、キーと値のペアを持つ実際のオブジェクトを返します。
{"number":"123-456-8888","type":"iPhone"} {"number":"123-557-8910","type":"home"}
文字列に改行が含まれる場合があるので、配列内にある要素の数を取得する1つの方法として、ValueCount( JSON ; KeyOrIndexOrPath )を使用することができます。上の電話番号の例では、2が返されます。スクリプトによるループで、繰り返しに変数($i)を設定し、次のようにデータを抽出します。
JSONGetElement( JSON ; "phoneNumbers[" & $i & "].number" )
インデックスはゼロスタートなので、ループのカウンタは1ではなく0から始まります。
まとめ
JSONを解析する方法を知るためには、まずJSONオブジェクトを取得することから始めます。WebサービスからJSON形式でFileMaker 16ファイルにデータを取得している場合、ネイティブの関数を利用できるようになりました。まずは、JSONオブジェクトを取得し、配列・ネストされたオブジェクト・キーと値のペアを探します。抽出したい要素を特定し、パスを決定して、式をテストしてJSONの内容と一致する値が取得できることを確認します。この記事とともに、シンプルなサンプルファイルを準備しました。JSONオブジェクトを貼り付けて、JSONGetElementを使用してすべてのデータ型をJSON形式で抽出することができます。
デモファイルを入手する
- Parse JSON demo file をダウンロード (訳注: コンタクト情報の入力が必要です)