JSON 関数の使い方

FileMaker Pro には、REST API を使用して JSON 形式のデータを転送する Web サービスなど、他の FileMaker 関数または他のソースの JSON データをカスタム App で解析および変更できるようにするテキスト関数がいくつか用意されています。

JSON データ形式の詳細については、json.org を参照してください。

このトピックで示す例では製品一覧を REST AP I 経由で JSON 形式で顧客に公開しているベーカリーの JSON データを使用します。次の例では、本日の特売一覧を $$JSON 変数に格納された JSON データとして返します:

コピー
変数を設定 [$url ; "https://bakery.example.com/rest/api/products"]
コピー
URL から挿入 [ダイアログあり: オフ; ターゲット: $$JSON ; $url ; SSL 証明書の検証; cURL オプション: "--data 一覧 = 特売"]

$$JSON に格納されて返されるデータおよびこれらの例で使用するデータについては、JSON データの例を参照してください。

JSON データの書式設定

JSON データでは要素間のスペースや改行は必要ありません。ただし、問題をデバッグするときにデータを読みやすくするには、JSON データの例に示すようにタブや改行コード文字を追加する JSONFormatElements 関数を使用します。

JSON データの解析

次の JSON 関数を使用して JSON データを解析します。つまり、キー、値、JSON オブジェクト全体 (さらに処理が可能な配列) を取得します:

  • JSONGetElement – JSON データで要素 (オブジェクト、配列、または値) のクエリーを実行します

  • JSONListKeys – JSON データ内のオブジェクト名 (キー) または配列索引の一覧を表示します

  • JSONListValues – JSON データ内の値の一覧を表示します

これらの関数の 1 番目の引数 json では、処理対象の有効な JSON データを含むテキストフィールド、変数、またはテキスト式を指定します。

2 番目の引数キーまたは索引またはパスでは処理対象の JSON データの位置を指定します:

  • キー – JSON オブジェクト内のキーの名前

  • 索引 – JSON 配列内の要素の索引 (1 番目の要素の索引は 0)

  • パス – キー名、配列索引、またはこの両方の階層文字列

キーまたは索引またはパス引数ではドット表記とカッコ表記の 2 種類の構文がサポートされます。

キーまたは索引またはパス引数の構文

意味

ドット表記 カッコ表記

"."

""

1 番目の文字である場合はルートレベル (ドット表記ではオプション)

".[n]"

"[n]"

ルートレベルの配列の索引 n の位置にある要素

". 名前"

"['名前']"

ルートレベルの名前というオブジェクトのキー

".名前 A.名前 B.名前 C"

"['名前 A']['名前 B']['名前 C']"

名前 C というオブジェクト、名前 B および名前 A の子孫

".[3][2][1]名前 A[0]"

"[3][2][1]['名前 A'][0]"

名前 A オブジェクトの配列の 1 番目の要素、ネストされた配列セットの 3 番目のレベル

"[:]"

"[:]"

配列の最後の要素

"[+]"

"[+]"

配列の最後の要素の後の位置。JSONSetElement 関数で使用して配列の末尾に要素を追加します。

ドット表記とカッコ表記の違いとして、カッコ表記ではピリオド (.) を使用してキー名を区切るのではなく、シングルクォーテーション (') とカッコ ([]) でキー名を囲みます。キーまたは索引またはパスではどちらの表記も使用できます。ただし、キー名にピリオドが含まれている場合、JSON パーサにキー名全体を正しく識別させるためにカッコ表記を使用する必要があります。たとえば、JSON オブジェクトのルートにあるキーが "レイアウト.応答" である場合、キーまたは索引またはパスは "['レイアウト.応答']" になります。

次のサンプルスクリプトは JSON 配列内の製品ごとにレコードを作成します。$$JSON 変数に JSON データの例が格納されているとすると、スクリプトは JSONListValues を使用して製品配列の内容を値の一覧として取得し、ValueCount を使用して一覧内の製品の数を調べます。ループするたびに製品のレコードを作成し、GetValue を使用して一覧から製品の JSON オブジェクトを取得して、フィールドを JSONGetElement で取得した値に設定します。JSON 関数は渡された JSON オブジェクト全体を解析するので、何度も繰り返されるループ内の小さい JSON オブジェクトに JSON 関数を使用するとより効率的です。

コピー
変数を設定 [$ProductList ; 値:JSONListValues ($$JSON ; "ベーカリー.製品")]
変数を設定 [$ProductCount ; 値: ValueCount ($ProductList)]
変数を設定 [$i ; 値: 1 ]
If [$ProductCount > 0]
   Loop [フラッシュ: 常に]
      新規レコード/検索条件
      変数を設定 [$Product ; 値: GetValue ($ProductList ; $i)]
      フィールド設定 [製品::ID ; JSONGetElement ($Product ; "id")]
      フィールド設定 [製品::価格 ; JSONGetElement ($Product ; "価格")]
      フィールド設定 [製品::在庫 ; JSONGetElement ($Product ; "在庫")]
      レコード/検索条件確定 [ダイアログあり: オフ]
      変数を設定 [$i ; 値: $i + 1] 
      Exit Loop If [$i > $ProductCount]
   End Loop
End If

JSON データ要素の変更と追加

JSON データの値を変更する場合や要素を追加する場合は JSONSetElement 関数を使用します。この関数の json およびキーまたは索引またはパス引数はJSON データの解析で説明したとおりに機能します。キーまたは索引またはパスで既存の要素を指定した場合はその要素の値が変更され、要素が存在しない場合は新しい要素が追加されます。

JSONSetElement は指定された要素に引数を設定します。単純な文字列や数値から複雑なオブジェクトや配列まで、すべての有効な JSON 値を指定できます。

タイプ引数ではのデータタイプを指定して、JSON パーサが各データタイプを扱うときの厳格なルールに従うようにします。サポートされるデータタイプについては JSONSetElement 関数を参照してください。有効な JSON 要素としてすでにフォーマット済みの json にデータを挿入するには、typeJSONRaw に設定します。

次の例では新製品のキー/値ペアを空の JSON オブジェクトに追加します。次に新しいオブジェクトを $$JSON 変数の製品配列の末尾に追加します (JSON データの例を参照してください)。

コピー
変数を設定 [$NewProduct ; 値:
   JSONSetElement ("{}" ;
      ["id" ; "FB4" ; JSONString] ; 
      ["名前" ; "バニラケーキ" ; JSONString] ; 
      ["価格" ; 17.5 ; JSONNumber] ; 
      ["在庫" ; 12 ; JSONNumber] ; 
      ["カテゴリ" ; "ケーキ" ; JSONString] ; 
      ["特売" ; true ; JSONBoolean] 
   ) ]
変数を設定 [$NextIndex ; 値:
   ValueCount ( 
      JSONListKeys ($$JSON ; "ベーカリー.製品") 
   ) ] 
変数を設定 [$$JSON ; 値:
   JSONSetElement ( 
      $$JSON ; "ベーカリー.製品[" & $NextIndex & "]" ; $NewProduct ; 
      JSONObject 
   ) ]

JSON 要素を作成するもう一つの JSON 関数は JSONMakeArray 関数です。値の一覧を JSON 配列に変換します。異なる書式のデータを受け取るために、この関数では各値を区切る文字と使用する JSON データタイプを指定できます。

JSON データ要素の削除

要素を削除するには JSONDeleteElement 関数を使用します。この関数の json およびキーまたは索引またはパス引数はJSON データの解析で説明したとおりに機能します。キーまたは索引またはパス引数では json の既存の要素を指定する必要があります。

次の例では $$JSON 変数の "ID" キーの値が "FB3" の製品配列の要素を削除します (JSON データの例を参照してください)。

コピー
変数を設定 [$ProductCount ; 値:
   ValueCount ( 
      JSONListKeys ($$JSON ; "ベーカリー.製品") 
   ) ] 
変数を設定 [$i ; 値:0]
If [$ProductCount > 0]
   Loop [フラッシュ: 常に]
      変数を設定 [$ID ; 値:
         JSONGetElement ($$JSON ; "ベーカリー.製品[" & $i & "]id")]
      If [$ID = "FB3"]
         変数を設定 [$$JSON ; 値:
            JSONDeleteElement ($$JSON ; "ベーカリー.製品[" & $i & "]")]
         現在のスクリプト終了 [テキスト結果:0]
      End If
      変数を設定 [$i ; 値: $i + 1]
      Exit Loop If [$i ≥ $ProductCount]
   End Loop
End If

JSON のパフォーマンスの最適化

JSON 関数がテキスト入力を解析するたびに、JSON パーサは (テキストではなく) バイナリ表現を作成してその後の JSON の処理を高速化します。JSON を解析してキャッシュする自動的なメカニズムと明示的なメカニズムである JSONParse 関数があります。

自動的な JSON キャッシュの活用

解析には時間がかかるため、JSON 関数は最後に解析された JSON のバイナリ表現をメモリにキャッシュします。これにより、後で同じ JSON を再び解析する必要がなくなります。自動キャッシュは 1 つだけ保持できます。別の JSON 値が解析されると、以前にキャッシュされた JSON 値は破棄されます。解析された JSON はメモリ内 (変数、スクリプトの引数、および計算内) にのみ存在します。フィールドの値を設定するために使用された場合、フィールド内にテキストとして保存されます。

この例では行番号ごとに、$JSON1 と $JSON2 に最初に保存された JSON テキストがいつ解析されるまたは解析されないのかを示しています。

コピー
変数を設定 [$id ; JSONGetElement ($JSON1 ; "id")]
変数を設定 [$name ; JSONGetElement ($JSON1 ; "名前")]
変数を設定 [$price ; JSONGetElement ($JSON2 ; "価格")]
変数を設定 [$category ; JSONGetElement ($JSON1 ; "カテゴリ")]
  1. $JSON1 が解析されてバイナリ表現がキャッシュに保存されます。

  2. $JSON1 を再度解析する必要はありません。

  3. $JSON2 が解析されてキャッシュされ、以前にキャッシュされた値が置き換えられます。

  4. $JSON1 はキャッシュされなくなったため、再び解析されます。

自動キャッシュを活用するには、一度に 1 つの JSON 値を操作するのが最適です (上記の例では、$JSON1 の再解析を避けるため、3 行目と 4 行目を入れ替えます)。

JSONParse の使用

変数と自動キャッシュに JSON 値のバイナリ表現を解析して明示的に保存するには、JSONParse 関数を使用します。関数の json 引数は JSON データに評価される任意のテキスト式です。フィールド内の JSON 形式のテキスト、文字列、または変数に保存された解析済みの JSON のいずれかです。

次の例では JSON テキストを解析します:

コピー
変数を設定 [$JSON1 ; "{\"製品\": {\"id\": \"FB1\"}}"]
変数を設定 [$JSON1 ; JSONParse ($JSON1)]
  1. $JSON1 はテキスト表現のみ含まれ、次のように表示されます:

    {"製品": {"id": "FB1"}}

  2. JSONParse の使用後:

    • 自動キャッシュにはバイナリ表現が含まれています。

    • $JSON1 変数にはテキスト表現とバイナリ表現の両方が含まれています。

JSONParse が使用されていない場合、次の例では:

コピー
変数を設定 [$JSON1 ; "{\"製品\": {\"id\": \"FB1\"}}"]
変数を設定 [$JSON2 ; JSONGetElement ($JSON1 ; "製品")]
  1. 前述のとおり $JSON1 はテキスト表現のみです。

  2. JSONGetElement の使用後:

    • 自動キャッシュにはバイナリ表現が含まれています。

    • $JSON2 には {"id": "FB1"} というバイナリ表現が含まれています。

    • $JSON1 はテキスト表現のみのままです。テキストは解析されましたが、バイナリ表現は自動キャッシュ内のみです。

JSONParse を追加した場合と比較してください:

コピー
変数を設定 [$JSON1 ; "{\"製品\": {\"id\": \"FB1\"}}"]
変数を設定 [$JSON1 ; JSONParse ($JSON1)]
変数を設定 [$JSON2 ; JSONGetElement ($JSON1 ; "製品")]
  1. 前述のとおり $JSON1 はテキスト表現のみです。

  2. JSONParse の使用後:

    • 自動キャッシュにはバイナリ表現が含まれています。

    • $JSON1 にはテキスト表現とバイナリ表現の両方が含まれています。

  3. JSONGetElement の使用後:

    • $JSON2 には {"id": "FB1"} というバイナリ表現が含まれています。

      この時点では $JSON2 にテキスト表現が保存されていませんが、後で必要な場合にのみ生成されます。

JSONGetElement は 2 行目の $JSON1 でキャッシュされたバイナリ表現を使用できるため、3 行目では $JSON1 を解析する必要はありません。

バイナリ表現を得るためにテキストを解析するのと同じように、バイナリ JSON をテキストに変換するには時間がかかります。そのため、JSONGetElement や JSONSetElement などの JSON 関数の結果はバイナリ表現のみになります。テキスト表現は変数 ($JSON2 など) をフィールドに保存したときや、データビューアで変数を表示するときなど、必要な場合にのみ作成されます。

補助関数 JSONParsedState を使用して特定の JSON 値に解析済みデータが格納されているか、JSON が有効かどうか、またはJSON タイプを検出することができます。

ベストプラクティス

大きな JSON 構造を繰り返し処理する場合 (特にループで)、または JSON データ内の複数の要素を操作する場合、処理の順序がパフォーマンスに大きな違いをもたらす場合があります。

自動 JSON キャッシュを有効に活用するには、1 つのフィールドから JSON データをロードして作業し、別のフィールドに切り替えて作業し、また最初のフィールドに戻るという操作は避けます。JSON 関数が異なる JSON テキストを処理するたびに、以前にキャッシュされた JSON は破棄されるため、テキストの再度解析が必要になります。たとえば、次のような場合パフォーマンスが最も遅くなります:

コピー
# 非効率な例
変数を設定 [$namel ; 値: JSONGetElement (テーブル::JSON 1 ; "名前")]
変数を設定 [$name2 ; 値: JSONGetElement (テーブル::JSON 2 ; "名前")]
変数を設定 [$id1 ; 値: JSONGetElement (テーブル::JSON 1 ; "id")]
変数を設定 [$id2 ; 値: JSONGetElement (テーブル::JSON 2 ; "id")]

手順を並べ替えて、同じ JSON データのすべての作業を行ってから別の JSON データで作業すれば、自動キャッシュが適切に機能し、同じフィールドのデータを 2 回解析する必要がなくなります。

コピー
# より効率的な例
変数を設定 [$namel ; 値: JSONGetElement (テーブル::JSON 1 ; "名前")]
変数を設定 [$id1 ; 値: JSONGetElement (テーブル::JSON 1 ; "id")]
変数を設定 [$name2 ; 値: JSONGetElement (テーブル::JSON 2 ; "名前")]
変数を設定 [$id2 ; 値: JSONGetElement (テーブル::JSON 2 ; "id")]

最良かつ最も柔軟な方法は JSONParse 関数を使用して各ソースから JSON テキストを取得および解析し、変数に保存することです。この方法ではテキストが一度だけ解析されます。それらの JSON 関数はすでに解析済みのバイナリ表現を含む変数を使用できるため、その後の処理 (要素の取得または設定など) の順序は関係ありません。

コピー
# 最も効率的な例
変数を設定 [$JSON1 ; 値: JSONParse (テーブル::JSON 1)]
変数を設定 [$JSON2 ; 値: JSONParse (テーブル::JSON 2)]

変数を設定 [$namel ; 値: JSONGetElement (テーブル::JSON 1 ; "名前")]
変数を設定 [$name2 ; 値: JSONGetElement (テーブル::JSON 2 ; "名前")]
変数を設定 [$id1 ; 値: JSONGetElement (テーブル::JSON 1 ; "id")]
変数を設定 [$id2 ; 値: JSONGetElement (テーブル::JSON 2 ; "id")]

JSON データのエラー処理

json 引数の解析中にエラーが発生した場合は、JSON 関数は「?」に続けて JSON パーサからのエラーメッセージを返します。

たとえば、JSON データの例の「ベーカリー」キーの後に「:」が見つからない場合は、計算式

コピー
JSONGetElement ($$JSON ; "ベーカリー.製品[0]id")

は次のエラーメッセージを返します:

コピー
? * Line 3, Column 2
  Missing ':' after object member name
* Line 13, Column 5
  Extra non-whitespace after JSON value.

JSON データを使用する前に有効かどうかを判定するには、JSONFormatElements 関数を使用して先頭の文字が「?」であるかどうかをテストします。例:

コピー
変数を設定 [$result ; 値:JSONFormatElements ($$JSON)]
If [Left ($result ; 1) = "?"]
   # $$JSON に無効な JSON データが含まれています。
End If

または、JSON データを使用する前に有効かつ特定の JSON タイプであるかどうかを判断するには、JSONGetElementType 関数を使用します。たとえば、$$JSON が有効な JSON オブジェクトであるかどうかをテストするには:

コピー
変数を設定 [$result ; 値:JSONGetElementType ($$JSON, "")]
If [$result ≠ JSONObject]
    # $$JSON に無効な JSON データが含まれています。
End If

エラーを検出する別の方法は JSONParsedState 関数です。JSON データが解析済みで有効かどうかと、その場合の JSON データのタイプを返します。

コピー
変数を設定 [$result ; 値: JSONParse ($$JSON)]
変数を設定 [$ParsedState ; 値: JSONParsedState ($result)]
If [$ParsedState < 0]
    # $$JSON は解析済みですが無効な JSON データが含まれています。
Else If [ParsedState = 0]
    # $$JSON は解析されていません。
Else If [ParsedState > 0]
    # $$JSON は解析済みで有効です。$ParsedState は JSON タイプを示します。
    変数を設定 [$type ; 値: 
        Case (
          $ParsedState = JSONString ; "JSON タイプは JSONString" ;
          $ParsedState = JSONNumber ; "JSON タイプは JSONNumber" ;
          $ParsedState = JSONObject ; "JSON タイプは JSONObject" ;
          $ParsedState = JSONArray ; "JSON タイプは JSONArray" ;
          $ParsedState = JSONBoolean ; "JSON タイプは JSONBoolean" ;
          $ParsedState = JSONNull ; "JSON タイプは JSONNull"
        )
    ]
    カスタムダイアログを表示 [$type]
End If

JSON データの例

次の例では、JSON データに「ベーカリー」オブジェクトが含まれています。「ベーカリー」オブジェクトは 3 つの「製品」オブジェクトを含み、それぞれがキー/値ペアを複数持ちます。

コピー
{
    "ベーカリー"
    {
        "製品"
        [
            {
                "id" : "FB1",
                "名前" : "ドーナツ",
                "価格" : 1.99,
                "在庫" : 43,
                "カテゴリ" : "パン",
                "特売" : true
            },
            {
                "id" : "FB2",
                "価格" :  22.5,
                "名前" : "チョコレートケーキ",
                "在庫" : 23,
                "カテゴリ" : "ケーキ"
                "特売" :  true
            },
            {
                "id" : "FB3",
                "価格" : 3.95,
                "名前" : "バゲット",
                "在庫" : 34,
                "カテゴリ" : "パン"
                "特売" : true
            }
        ]
    }
}

メモ 

  • JSON パーサは配列内の要素の順序は維持しますが、オブジェクト内の要素の順序は維持しません。そのため、JSON 関数は指定した順序とは異なる順序でオブジェクトの要素を返すことがあります。

  • JSON データの分数値には小数点記号としてピリオド「.」を使用する必要があります。これは、コンピュータのシステム形式や FileMaker Pro ファイルの作成時に使用された形式で指定されている記号とは無関係です。