DataWeave in Apexを使うためのDataWeave入門
こんにちは。木村です。
この記事はWinter '24でGAになったDataWeave in Apexを使えるようになるための記事です。そのためにはDataWeave言語自体を知る必要があるので最低限押さえておく必要があることを解説し、最後にApexで使うと便利そうなユースケースも合わせて紹介します。
対象読者はSalesforceコアプラットフォームエンジニアでDataWeaveを知らない方です。と言っても私もDataWeave初心者ですが…
なお、この記事は2月28日のゆるっとSalesforceトーク #28で発表した内容です。
DataWeaveとは?
DataWeaveはMuleSoftで使用されているデータ変換用の関数型プログラミング言語です。ある形式のデータを読み込み、変換処理を行い、別の形式にして出力する処理に特化した言語となっています。DataWeaveでは入出力データを標準モデル(Canonical Model)に変換し、DWスクリプト上ではどんな入出力でも標準モデルのデータとして扱います。(下図)
例えば、以下のようにCSVファイルを読み込んで、少し計算を加えてJSONの配列に変換することができます。
入力CSV
DWスクリプト
出力JSON
DataWeaveの実行環境
DWスクリプトを試行錯誤するにはログイン不要で使用できるPlaygroundが便利です。
上述のCSV→JSONの例も以下のように実行できます。
他にも以下のような実行環境があります。
DataWeave in Apexとは?
DataWeave in ApexはこのDWスクリプトをApexから実行できる機能です。MuleSoftは不要でSalesforceプラットフォームのみで動作します。
例えば、以下のDWスクリプトでCSVを取引先責任者レコードに変換できます。
このファイルはDataWeaveResourceメタデータとして force-app/main/default/dw/csvToContacts.dwl のような場所に配置し、組織にpushします。
Apexでは以下のように呼び出します。「DataWeaveScriptResource.<DWスクリプト名>」のクラスが自動的に使えるようになっており、これをnewして、executeメソッドを呼ぶだけです。executeメソッドの引数がDWスクリプトの入力データ(input)となります。
このようにApexから呼ぶこと自体は簡単なので、DataWeave in Apexを使いこなすにはDataWeave自体の知識が必要になります。
DataWeave言語の文法
データ型・演算子・変数・関数・条件分岐・組み込み関数のように一般的なプログラミング言語にあるような機能は備えています。また、標準モデルのデータにアクセスするためのセレクタを備えていることが特徴的です。
Playgroudのメニューにあるチュートリアルを一通りやると一通りの機能が理解できると思います。ここではいくつか特徴的な文法・機能を抜粋します。
その他の文法・機能は言語ガイドやリファレンスをご覧ください。
DWスクリプトの構成
このDWスクリプトを例に説明すると、以下のような5つのエリアから構成されています。
1行目はバージョン指定です。これはおまじないと思えばよいです。
2行目は入力データの変数名とMIMEタイプを指定します。(MuleSoft上で動く場合は不要。DataWeave in Apexでは必ず指定し、Apex側の引数と対応します。)
3行目は出力データのMIMEタイプを指定します。
4行目からハイフン3つの前で変数や関数を定義します。
ハイフン3つの後にデータ変換/出力処理を書きます。必ず出力を返すのでreturnは存在しません。
セレクタ
標準モデルの各要素にアクセスする文法がセレクタです。標準モデルはJSONに似ており、取得方法もJavaScriptでオブジェクトの値を取得するのと非常に似ています。
ただし、以下のように特殊なセレクタがいくつかあります。
複数値セレクタ: .*keyNameのように指定するとその階層の全てのidの値の配列を取得
子孫セレクタ: ..keyNameのように指定すると子孫の全てのidの値の配列を取得
また見てわかるようにJSONとは異なり、同階層に同じキーを持つことができるのも特徴的なところです。
関数
カスタム関数を定義することもできます。一般的な名前付き関数に加えて、JavaScriptのアロー関数のようなラムダという記法もサポートしています。
以下の例は2つの定義方法と2つの呼び出し方の例です。
関数定義
「addNamed」は通常の関数名ありの定義方法
「addLambda」はラムダという無名関数の定義方法
関数呼び出し
「addNames(2, 3)」は普通の関数呼び出しです。
「2 addNamed 3」は中記表記(infix notation)と呼ばれる関数呼び出し方法です。配列/オブジェクト操作で多用します。
また、組み込み関数も多数用意されています。よく用意されているような文字列操作・日付操作・数値処理などはだいたい用意されています。
配列/オブジェクト操作
データ変換では配列とオブジェクトの操作はかなり頻度が高く行う処理です。JavaScriptにおけるfilter/map/reduceと同等の操作に加えて、便利な関数が数多く用意されています。
distinctBy
配列から指定キーで重複を除去します。orderId+lineIdで重複を除去しています。
また、「shoppingList distinctBy ($.orderId ++ $.lineId)」と省略記法で書くこともできます。このとき$はラムダの第一引数を意味します。
mapObject
配列用のmap関数のオブジェクト版です。オブジェクトを新しいオブジェクトに変換します。
以下の例では、オブジェクトのキーを大文字に変換しています。
pluck
pluckはオブジェクトを配列に変換する関数です。
groupByとよく一緒に使われます。groupByは配列をグループ分けしたオブジェクトに変換する関数です。以下の例ではそれをさらにpluckで配列の配列に変換しています。
関数呼び出しの中記記法はこのようなデータを連続して変換していくときに便利に使えます。
その他にも便利な関数が提供されていますので、詳しくはドキュメントをご覧ください。
DataWeave in Apexの便利そうな使い方
CSVの解析
CSVのパースを厳密に行うのはけっこう難しいですが、Apexには専用クラスが提供されていないので自作するしかありません。DataWeaveを使うとほとんどのCSVファイルを正しく解析してくれます(たぶん)。
CSV入力の例は「DataWeave in Apexとは?」で紹介したコードと同じですが、以下に再掲します。CSVを読み込んでContactレコードに変換しています。
Apex予約語を含むAPIレスポンスの変換
例えば、APIレスポンスにcurrencyやprivateといったApex予約語を含む場合どう処理しますか?Apexだけだと予約語のキー名のプロパティ変数を作れないのでJSON.deserializeではクラスに変換できません。
こういうときに以下のようなDWスクリプトでApex予約語をcurrency→currency_xのようにエスケープすることができます。
これを使えば以下のようにいきなりクラスインスタンスに変換できます。
JSON出力時のカスタム日付フォーマット
JSON.serializeだと日付フォーマットは必ずISO 8601形式(2011-03-22T08:15:18.000Z)になります。他の形式にしたい場合は一苦労です。
DataWeaveを使うと以下のようなスクリプトで日時を好きなフォーマットに変換しつつ、その他はそのままJSONとして出力するという処理も可能になります。
multipart/form-dataの処理
DataWeaveはマルチパート (フォームデータ) 形式のように簡単にマルチパート形式を扱えます。Apexは苦手な処理なので、向いていると思ったのですが、「System.UnexpectedException: Salesforce System Error: 41512346-28387 (63708964) (63708964)」とエラーになってしまい、動きませんでした😭
DataWeave in Apexの制限
DataWeave in Apexの代表的な制限は以下の通りです。その他の制限はLimitations of DataWeave in Apexをご覧ください。
Excelフォーマットは未サポート
追加ライブラリ・他のスクリプトのインポートは禁止
DWスクリプトは組織ごとに最大50個(AppExchangeアプリの個別枠はなし)
ガバナ制限対象になるのか?
イベントでも話題になったのですが、ApexのヒープやCPUのガバナ制限の対象になるのかは、特にドキュメントに記載もなく現状よくわかっていません。
convertLargeCsvToObjectsという2万件のレコードを変換するサンプルコードがあるのですが、これを実行すると一度ヒープサイズエラーになりました。また、スクリプト実行後に以下のようにヒープサイズ・CPU使用時間ともに相応に消費していたので、ガバナ制限対象になりそうです。
14:15:11.476 (5716614975)|USER_DEBUG|[51]|WARN|After parse Heap: 1937401/6000000
14:15:11.476 (5716768766)|USER_DEBUG|[52]|WARN|After parse CPU: 4900/10000
おわりに
どうでしたでしょうか?私はCSV処理・APIリクエスト/レスポンスの処理・日付フォーマット・データ形式の変換・multipartの処理のようなApexの苦手な処理が少ないコードで厳密に簡単に書けるのでけっこう使えそうな気がしています。
SIならどんどん使っていっていいのではないでしょうか。ただ、AppExchangeアプリで採用するかというと組織での50スクリプトの制限が悩ましいなあというところですね。