JSONataは、APIから返されたJSONレスポンス本文のデータにアクセスし、解析するためにJSONデータ用に設計されたオープンソースのクエリおよび変換言語です。
クエリは、https://try.jsonata.org/を使用して簡単にテストできます。Javascript的な書式が必要な場合は、https://www.stedi.com/jsonata/playgroundを使用してください。ここでは、JSONata独自のExerciserについて説明します。
Exerciserは、参照ポイントとして使用する請求書ダミーオブジェクトを起動します。未加工の入力データが左側に、JSONata式クエリが右上にあり、その式で見つかった結果が右下に表示されます。
JSONオブジェクトを再配置するには、ダミーオブジェクトの右側にある小さなインデントツールを使用し、右上の共有機能を使用して現在の式を保存します。
Exerciserについては以上です。次に、デフォルトの請求書テンプレート内でクエリを使用します。
有効にした後の重要な学習事項や参照ポイントについては、チートシートを参照してください。
データのマッピング
ルートレベルのフィールド:アカウント
ルートオブジェクトのネストされたフィールド:Account.'Account Name'
別のJSON構造が見つかった場合は、ドット表記を使用して以下のフィールドに入力できます。
ルートレベル配列のネストされたフィールド:
このアカウントの最初の注文:
Account.Order[0]
お知らせ:配列のインデックスは、最初の項目の[0]から始まります。配列の最後の項目へのショートカットは[-1]です。
同じオブジェクト内の最初の製品の色:
Account.Order[0].Product[0].Description.Colour
ルートレベル配列から項目の配列を取得する:
複数の値がクエリに一致する場合、JSONataはそれらの値を自動的に集計します。
Account.Orderオブジェクト全体を確認し、その中のすべてのOrderIDを取得します。
Account.Order.OrderID
データのフィルター
アクセスするターゲットを定義します。例:
Account.Order.OrderID
次に、条件を設定する適切な場所に[ ]を追加します。
例:価格が30を超える数量1個の注文のみをフィルターして、製品名を表示します。
Account.Order.Product[Price > 30 and Quantity = 1].'Product Name'
例:説明付き製品を含む注文のみをフィルターします。重みは1以上で、それぞれのOrderIDを示します。
Account.Order[Product[Description.Weight > 1]].OrderID
または、真のステートメントも確認できます。
Account.Order[Product["Purple" in Description.Colour]].OrderID
さらに、複数のキーを確認したい場合は、ワイルドカード検索と組み合わせることができます。
Account.Order[Product["Purple" in Description.*]].OrderID
その他のパス演算子
(https://docs.jsonata.org/path-operators)
^( ... )(表示順)
OrderIDを降順で並べ替え:
Account.Order^(>OrderID)
安価な製品順で並べ替え:
Account.Order.Product^(Price)
* (ワイルドカード)
直接の親の名前に関係なく、任意のSKUにアクセスします。
Account.Order.*.SKU
親の名前に関係なく、任意の製品名にアクセスします。
**.'Product Name'
% (親)
現在のデータ構造を逆方向に検索:
Account.Order.Product.{
'Account': %.%.`Account Name`,
'Order': %.OrderID,
'Product': `Product Name`
}
#(位置変数のバインド)
0から始まるインデックスを作成:
Account.Order#$i[Product.[Quantity > 0]].{
'Order ID': OrderID,
'Order Number': $i + 1
}
@(コンテキスト変数のバインド)
新しいデータ構造を一時的に割り当て、オブジェクト間のマッピングを可能にします。
Account@$A.Account.Order@$O.{
"Account Name": $A.'Account Name',
"OrderID": $O.OrderID
}
条件文
JSの三項演算子と同様に、if文には?、else文には:を使用できます。ブール演算子やand/orを使用して、連鎖条件を作成できます。
IF CONDITION IS TRUTHY ?DO THIS (ELSE DO NOTHING)
IF CONDITION IS TRUTHY ?DO THIS :ELSE DO THIS
$count(Account.Order) > 1 ? "REPEAT CUSTOMER"
Account.Order[0].Product[0].Price <= 100 or Account.Order[0].Product[1].Price <= 100 ? "Bargain" : "VIP" = "Bargain"
データの操作
サポートしている演算子
演算子 | 優先度 | 説明 |
乗算(*) | 5 | 2つの数値を乗算します。 |
除算(/) | 5 | 2つの数値を除算します。 |
剰余(%) | 5 | 2つの数値を除算したときの余りを返します。 |
連結(&) | 4 | 2つの文字列を連結します。 |
加算(+) | 4 | 2つの数値を加算します。 |
減算(-) | 4 | 2つの数値を減算します。 |
等しい(=) | 3 | 2つの値が等しいかテストします。 |
等しくない(!=) | 3 | 2つの値が等しくないかテストします。 |
より大きい(>) | 3 | 左の値が右の値より大きい場合、trueを返します。 |
以上(>=) | 3 | 左の値が右の値以上の場合、trueを返します。 |
より小さい(<) | 3 | 左の値が右の値より小さい場合、trueを返します。 |
以下(<=) | 3 | 左の値が右の値以下である場合、trueを返します。 |
論理積(and) | 2 | 左と右の値が両方ともtrueの場合、trueを返します。 |
論理和(or) | 1 | 左の値または右の値がtrueの場合、trueを返します。 |
例:文字列の連結
Account.Order[0].Product[0].Description.Colour & " " & Account.Order[0].Product[0].'Product Name'
組み込み関数
読みやすくするために、これらの説明は簡潔にまとめられています。詳細なドキュメントについては、https://docs.jsonata.org/overview.htmlを参照してください。
メモ:複数行の式は()で囲む必要があります。
文字列のタイプを強制: $string(Account.Order[0].Product[0].ProductID):"858383"
数値のタイプを強制: $number(Account.Order[0].Product[0].SKU):406654608
文字列を大文字に変換: $uppercase(Account.Order[0].OrderID):"ORDER103"
文字列を小文字に変換: $lowercase(Account.Order[0].Product[0].Description.Colour): "purple"
0~1の範囲の乱数を出力:$random()
配列内のオブジェクトをカウント: $count(Account.Order)
条件に一致する配列内のオブジェクトをカウント: $count(Account.Order.Product[Price > 30])
文字列の長さを取得: $length(Account.'Account Name')
文字列内の特定の文字を置換/削除: $replace(Account.Order[0].Product[0].'Product Name', "Bowler ", ""):"Hat"
特定の文字数で文字列を切り出す:$substring($string(Account.Order[0].Product[0].ProductID), 1, 2)
特定のパターンの文字列を切り出す:$substringAfter(Account.Order[0].OrderID, "order")
splitとjoinを使用した場合も同様の結果になります。
$join($split(Account.Order[0].Product[0].'Product Name', " "), '_')
~>関数として記述することもできます。
$split(Account.Order[0].Product[0].'Product Name', " ") ~> $join('_')
文字列に特定のパターンが含まれているかをチェックします。パターンは、正確な文字列または正規表現です。
$contains(Account.Order[0].Product[0].'Product Name', "Hat")
日付と時刻
ほとんどの日付はISO 8601国際規格に従って渡され、2023-04-20T13:09:39+00:00(タイムゾーン情報)または2023-04-20T13:09:39Z(UTCからのミリ秒オフセットをキーとする)のようになります。これらは人間でも読み取り可能ですが、JSONataではネイティブに操作できません。
したがって、多くの場合、UNIX時間に変換する必要があります。UNIX時間とは、1970年1月1日00:00:00 UTC(Unixエポック時間)から経過した秒数のことです。この値は、人間が読み取ることはできませんが、整数として他のUNIX日付と加算/減算/比較することができます。JSONataは、UNIXエポック(UNIX * 1000)以降のミリ秒単位であるMillisとネイティブに連携します。
$now():"2023-04-20T13:39:58.216Z"
$millis():1681998518175
これらのMillisは、画像(ターゲットパターン)を文字列で定義することで、任意の日付形式に再変換できるようになりました。https://www.w3.org/TR/xpath-functions-31/#date-picture-string
パターンの例:https://www.w3.org/TR/xpath-functions-31/#date-time-examples
指定子 | 意味 |
Y |
年(絶対値) |
M | 1年の月数 |
D | 1月の日数 |
F | 1週間の日数 |
H | 1日の時間数(24時間) |
h | 半日の時間数(12時間) |
P | AM/PMマーカー |
m | 時間の分 |
s | 分の秒 |
Z | タイムゾーン |
例: $fromMillis($millis(), '[M]/[D]/[Y01]')は、"4/20/23"を返します。
$fromMillis($millis(), '[D01].[M01].[Y0001] [H#1]:[m01]')は、"20.04.2023 13:51"を返します。
これを実行すると、応答データに2つの日付があり、今日から何日経ったかを確認できます。
"2023-04-20T00:00:00.000Z"
"20.04.2023"
これらをMilisに変換します。2番目の日付はISO 8601標準規格のパターンではないため、どの値が日、月、年、および利用可能な時間情報かをJSONataが判断できるように、画像を提供する必要があります。
$toMillis('2023-04-20T00:00:00.000Z') :1681948800000
$toMillis('20.04.2023', '[D01].[M01].[Y0001]'):1681948800000
また、標準関数$millis():1681999968402を使用して、現在の時刻をミリ秒単位で取得することもできます。
片方の日付からもう片方の日付を減算するだけで、2つの日付の差をミリ秒単位で取得することができます。1681999968402 - 1681948800000 = 51296367
共通時間変換の使用:
1000ミリ秒 = 1秒
60秒 = 1分
60分 = 1時間
結果を切り捨てると、現在から指定したタイムスタンプまでに経過した合計14時間を取得します。
$round(51296367 / 1000 / 60 / 60) = 14
次のように1行に結合できます。
$round(($millis() - $toMillis('20.04.2023', '[D01].[M01].[Y0001]')) / 1000 / 60 / 60)
特別な出力
チケット「Oneliners」
返信が1回のみの場合、1つのパラメータでできるだけ多くの情報を渡す必要があります。JSONataが集計を処理するので、テキストを自由にコピーできます。
注文したすべての製品のSKUと価格を表示する場合は以下のようにします。
Account.Order.Product
最終的にすべての製品を考慮した長い1行のテキストにする場合は、必要なすべての情報を含む1つの共有配列を作成できます。
Account.Order.Product.(SKU & Price)
次に、区切り文字として改行文字を含む1つの長い文字列に結合します。
$join(Account.Order.Product.("SKUs: " & SKU & ", " & "Price: " & Price), "\n")
メモ:CRMの制限事項を考慮してください。Zendesk Supportには基本的なフォーマットが用意されているため、返信メールで\nを使用すると正しく改行に変換されますが、すべてのシステムに当てはまるとは限りません。
カードとカルーセル
カードとカルーセルは、レスポンスが同じスキーマに従っていれば、データのクエリと集計をネイティブに処理するため、JSONataの真の強みです。カードとカルーセルは、各オブジェクトが1枚のカードを表すオブジェクトの配列で構成されます。2枚のカードを使用したカードとカルーセル構造の非常にシンプルな例は、次のようになります。
[
{
"imageURL": data.url1,
"title": data.title1,
"description": data.description1
},
{
"imageURL": data.url2,
"title": data.title2,
"description": data.description2
}
]
重要な点は、配列内のすべてのオブジェクトが同じ構造に従っているため、同じ共有キーでアクセスされる可能性があるということです。また、CRMの制限事項にも注意してください。
簡単な例として、請求書のサンプルデータでもう一度カードとカルーセルを作成してみましょう。まず、配列から始めましょう。複数のレスポンスオブジェクトを含むクエリの結果は自動的に配列に変換されますが、結果が1つのオブジェクトしか持たない場合に備えて、このフェイルセーフを設定することをお勧めします。
[Account.Order.Product.'Product Name']
[Account.Order.Product.SKU]
[Account.Order.Product.Description.Colour]
いくつかのフィールドを1つのオブジェクトにまとめましょう。ターゲットオブジェクトの各キーには必ず名前を付けてください。
[
Account.Order.Product.
{
"name": 'Product Name',
"sku": SKU,
"colour": Description.Colour
}
]
1レベル上の値が必要な場合は、単純に1レベル上のクエリを開始できます。配列に[Account.Order.OrderID]を含める場合:
[
Account.Order.
{
"orderId": OrderID,
"name": Product.'Product Name',
"sku": Product.SKU,
"Colour": Product.Description.Colour
}
]
各注文に複数の項目を含むことができるため、1対1で直接一致するものはなく、このレスポンスによって一見ランダムな文字列配列が作成されることが分かります。これは、親オブジェクトに間接的にアクセスしたり(親バインディングを参照)、JSONataに別のオブジェクト構造を一時的に割り当てたり(コンテキスト変数バインディングを参照)、選択した注文で項目を循環させる第2のカードとカルーセルを追加したり、理想的なUXに応じてデータをさらに変換することで解決できます。以下は変換の例です。
[
Account.Order.
{
"orderId": OrderID,
"name": $join(Product.'Product Name', ", "),
"totalPrice": $sum(Product.Price)
}
]
クエリをさらに箇条書きにしたい場合は、期待の応答データがない場合のフェイルセーフ(404など)と、CRMがサポートできない結果(SunCoウィジェットの場合は10など)が返される場合のカットオフを追加します。
Account.Order ?
[Account.Order[[0..9]].
{
"orderId": OrderID,
"name": $join(Product.'Product Name', ", "),
"totalPrice": $sum(Product.Price)}]
: [{"orderId": "???", "name": "Cannot find your item?"}]
重要:すべてのカードとカルーセルのキーがcamelCaseの表記法に従っていることを確認してください。_パラメータ名はサポートされていません。
これで、orderId、name、totalPriceなどのすべてのキーがカルーセルで利用できます。UIでカルーセルタイプを動的に変更し、上記のクエリをホストするパラメータ名(例:orderList)を追加します。
「%」を追加して、カード上のチャットに直接出力したり、
選択したオブジェクトのキーを参照するアクション付きのカードを、選択したパラメータ名に入力してセッションに保存して、配列のオブジェクト内の任意のキーにアクセスできます。例: