0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

rulemorph v2リファレンス

0
Last updated at Posted at 2026-01-25

rulemorph リファレンス (v2)

YAML定義に基づいてCSV/JSONをJSONに変換するツールのリファレンスです。

▼ プレイグラウンド

目次


早見表

ルールファイル構造

version: 2
input:
  format: csv | json
  csv: { ... }
  json: { ... }
output:
  name: "TypeName"
record_when:
  gt: ["@input.score", 10]
mappings:
  - target: "..."
    source: "..."
フィールド 必須 説明
version ○ 固定値 2
input ○ 入力形式と設定
output - メタ情報(DTO生成時の型名など)
record_when - レコードフィルタ条件(@input.*/@context.*のみ参照可)
mappings ○ 変換ルール(上から順に評価)

入力形式(CSV/JSON)

CSV設定

input:
  format: csv
  csv:
    has_header: true
    delimiter: ","
    columns:        # has_header=false 時に必須
      - { name: "id", type: "string" }
      - { name: "price", type: "float" }
オプション 既定値 説明
has_header true ヘッダー行の有無
delimiter "," 区切り文字(1文字のみ)
columns - has_header=false 時の列定義(必須)

JSON設定

input:
  format: json
  json:
    records_path: "data.items"
オプション 既定値 説明
records_path - レコード配列へのドットパス(省略時はルート)

注意: records_path がオブジェクトを指す場合は、単一レコードとして処理されます。


マッピングオプション

mappings:
  - target: "user.id"
    source: "id"
    type: "string"
    required: true
    default: "unknown"
    when:
      eq: ["@input.active", true]
オプション 必須 説明
target ○ 出力先のドットパス
source △ 入力参照パス(value/exprと排他)
value △ リテラル値(source/exprと排他)
expr △ パイプ式(source/valueと排他)
when - 条件式(falseでスキップ)
type - 型キャスト(string/int/float/bool)
required - 必須フラグ(既定false)
default - missing時のデフォルト値

注意: source/value/expr はいずれか1つを指定する必要があります。


演算子一覧

文字列操作

op 引数 説明
concat >=1 文字列連結
to_string 0 文字列化
trim 0 前後空白削除
lowercase 0 小文字化
uppercase 0 大文字化
replace 2-3 文字列置換
split 1 分割して配列化
pad_start 1-2 先頭埋め
pad_end 1-2 末尾埋め

数値演算

op 引数 説明
+ / add >=1 加算
- >=1 減算
* / multiply >=1 乗算
/ >=1 除算
round 0-1 四捨五入
to_base 1 N進数変換

日付処理

op 引数 説明
date_format 1-3 日付フォーマット変換
to_unixtime 0-2 Unix時間へ変換

論理演算

op 引数 説明
and >=1 論理AND
or >=1 論理OR
not 0 論理NOT

比較演算

op 引数 説明
== 1 等価
!= 1 非等価
< 1 小なり
<= 1 以下
> 1 大なり
>= 1 以上
~= 1 正規表現マッチ

検索・フォールバック

op 引数 説明
coalesce >=1 最初の非null/missing値
lookup 2-4 配列検索(全件)
lookup_first 2-4 配列検索(先頭)

JSON操作

op 引数 説明
merge >=1 オブジェクトのシャローマージ
deep_merge >=1 オブジェクトのディープマージ
get 1 ドットパスで値を取得
pick >=1 指定パスのみ抽出
omit >=1 指定パスを除外
keys 0 オブジェクトのキー配列
values 0 オブジェクトの値配列
entries 0 {key, value} の配列
len 0 文字列/配列/オブジェクトの長さ
from_entries >=1 エントリからオブジェクト構築
object_flatten 1 ネストをフラット化
object_unflatten 1 フラットをネストに復元

配列操作(変換)

op 引数 説明
map 1 各要素を変換
filter 1 条件に一致する要素のみ
flat_map 1 変換後フラット化
flatten 0-1 配列のフラット化

配列操作(抽出)

op 引数 説明
first 0 先頭要素
last 0 末尾要素
take 1 先頭/末尾からN件取得
drop 1 先頭/末尾からN件除外
slice 1-2 範囲指定で取得
chunk 1 N件ずつ分割

配列操作(結合)

op 引数 説明
zip >=1 複数配列をペア配列に
zip_with >=2 複数配列を結合関数で結合
unzip 0 ペア配列を複数配列に分離

配列操作(グループ化)

op 引数 説明
group_by 1 キーでグループ化(オブジェクト)
key_by 1 キーでインデックス化(1件のみ)
partition 1 条件で2分割

配列操作(重複処理)

op 引数 説明
unique 0 重複除去(順序保持)
distinct_by 1 キー基準で重複除去

配列操作(ソート・検索)

op 引数 説明
sort_by 1 キー基準で昇順ソート
find 1 条件に一致する最初の要素
find_index 1 条件に一致する最初のインデックス
index_of 1 値のインデックスを取得
contains 1 値が含まれるか判定

配列操作(集計)

op 引数 説明
sum 0 合計
avg 0 平均
min 0 最小値
max 0 最大値
reduce 1 畳み込み(初期値=最初の要素)
fold 2 畳み込み(初期値指定)

型変換

op 引数 説明
string 0 文字列に変換
int 0 整数に変換
float 0 浮動小数点に変換
bool 0 真偽値に変換

詳細解説

参照(Reference)

参照は @ 付きの名前空間 + ドットパスで指定します。

5つの名前空間

名前空間 説明 例
@input.* 入力レコード @input.user.name
@context.* 実行時注入の外部データ @context.tenant_id
@out.* 先行マッピングの結果 @out.fullName
@item / @item.* map/filter内の現在要素 @item, @item.name, @item.index
@<var> letで束縛した変数 @total, @base

特別な参照

参照 説明
$ 現在のパイプ値(直前のステップの結果)
@item map/filter内の現在の配列要素
@item.index 現在の要素のインデックス(0始まり)

source での省略ルール

source では 単一キー の場合のみ名前空間を省略できます。

# OK: 単一キーは省略可能
- target: "id"
  source: "id"          # @input.id と同等

# ドットパスや配列インデックスは明示的に指定
- target: "name"
  source: "input.user.name"

expr では名前空間を明示

expr 内では常に @ 付きで名前空間を明示します。

# OK
expr:
  - "@input.id"

# NG(エラー)
expr:
  - "id"

リテラル文字列として @ を使用

@ や $ をリテラルとして扱う場合は lit: プレフィックスを使用します。

expr:
  - "lit:@example.com"    # 文字列 "@example.com"

式(expr)パイプ構文

expr はパイプ配列形式で記述します。左から右へ順に評価され、各ステップの結果が次のステップの入力となります。

基本構文

expr:
  - "@input.name"     # 開始値
  - trim              # ステップ1: 前後空白削除
  - uppercase         # ステップ2: 大文字化

入力: {"name": " alice "}
出力: "ALICE"

引数付きオペレーション

expr:
  - "@input.price"
  - multiply: [1.1]   # price × 1.1
  - round: [2]        # 小数第2位で丸め

入力: {"price": 100}
出力: 110.0

単一参照の省略形

パイプが参照のみの場合は配列を省略できます。

# 配列形式
expr:
  - "@input.name"

# 省略形
expr: "@input.name"

ネストしたオペレーション

引数内で別のパイプを使用できます。

expr:
  - "@input.base_price"
  - multiply: ["@context.exchange_rate"]

入力: {"base_price": 100}, コンテキスト: {"exchange_rate": 1.5}
出力: 150.0


ステップ

パイプ内で使用できるステップの種類です。

Op ステップ(オペレーション)

# 引数なし
- trim
- uppercase

# 引数あり(短縮形)
- multiply: [2]
- concat: ["@out.suffix"]

# 引数あり(明示形)
- { op: "replace", args: ["old", "new", "all"] }

Let ステップ(変数束縛)

中間結果を変数に保存し、後続のステップで参照できます。

expr:
  - "@input.price"
  - let: { base: "$" }           # $(現在値)を base に束縛
  - multiply: [1.1]
  - let: { with_tax: "$" }       # 税込価格を with_tax に束縛
  - if:
      cond:
        gt: ["@base", 100]       # base を参照
      then:
        - "@with_tax"
        - multiply: [0.9]        # 割引適用
      else:
        - "@with_tax"

If ステップ(条件分岐)

expr:
  - "@input.price"
  - if:
      cond:
        gt: ["$", 100]           # 条件: 100より大きい
      then:
        - "$"
        - multiply: [0.9]        # 10%割引
      else:
        - "$"                    # そのまま

入力: {"price": 150}
出力: 135.0

Map ステップ(配列変換)

配列の各要素にパイプを適用します。@item で現在の要素を参照します。

expr:
  - "@input.items"
  - map:
    - multiply: [2]              # 各要素を2倍

入力: {"items": [1, 2, 3]}
出力: [2.0, 4.0, 6.0]

@item を明示的に使用する例:

expr:
  - "@input.users"
  - map:
    - "@item.name"               # 各ユーザーの name を抽出
    - uppercase

let と組み合わせた例:

expr:
  - "@input.items"
  - map:
    - let: { doubled: ["$", multiply: [2]] }
    - "@doubled"

入力: {"items": [1, 2, 3]}
出力: [2.0, 4.0, 6.0]


条件構文(Conditions)

record_when、when、if.cond で使用する条件構文です。

比較演算子

条件 説明
eq: [a, b] 等価(厳密一致、型も比較)
ne: [a, b] 非等価
gt: [a, b] a > b
gte: [a, b] a >= b
lt: [a, b] a < b
lte: [a, b] a <= b
match: [str, regex] 正規表現マッチ

注意: eq/ne は JSON の厳密一致です。"1" と 1 は一致しません。

論理演算子

条件 説明
all: [cond1, cond2, ...] AND(すべて true)
any: [cond1, cond2, ...] OR(いずれか true)

例

# 単純な比較
when:
  eq: ["@input.status", "active"]

# 数値比較
when:
  gt: ["@input.score", 80]

# 正規表現マッチ
when:
  match: ["@input.email", ".*@example\\.com$"]

# 複合条件(AND)
when:
  all:
    - gt: ["@input.age", 18]
    - eq: ["@input.verified", true]

# 複合条件(OR)
when:
  any:
    - eq: ["@input.tier", "premium"]
    - eq: ["@input.tier", "gold"]

ドットパス構文

ドットパスは以下の構文をサポートします。

構文 説明 例
field.nested ネストしたオブジェクト @input.user.profile
array[N] 配列の要素(0始まり) @input.items[0]
field["key"] ドットを含むキー @input.user["profile.name"]

配列インデックス

# 配列の要素にアクセス
expr:
  - "@input.items[0].id"

# 多次元配列
expr:
  - "@context.matrix[1][0]"

注意: 範囲外インデックスや存在しないパスは missing として扱われます。


missing vs null

rulemorph では「参照先が存在しない」状態と「値が null」の状態を区別します。

状態 意味 例
missing 参照先が存在しない キーがない、配列外参照
null 参照先が存在し値が null {"name": null}

default の適用ルール

default は missing のときのみ 適用されます。null には適用されません。

# missing の場合: default が適用される
# null の場合: null のまま
- target: "status"
  source: "status"
  default: "unknown"

required/default 挙動表

required default 値がmissing 値がnull
false なし フィールド生成されない null
false あり default値 null
true なし エラー エラー
true あり default値 エラー

ポイント: null は「存在する」ので default は適用されませんが、required=true ではエラーになります。


型キャスト変換ルール

type オプションまたは型変換オペレーションで出力値の型を指定できます。

変換先 変換元 備考
string string/number/bool そのまま文字列化
int number/数値文字列 1.0 → OK、1.1 → NG
float number/数値文字列 NaN/Infinity → NG
bool bool/"true"/"false" 大文字小文字無視
# mapping の type オプション
- target: "count"
  source: "count"
  type: "int"

# パイプ内の型変換オペレーション
- target: "id"
  expr:
    - "@input.id"
    - string

record_when レコードフィルタリング

record_when はルートレベルで指定し、マッピング処理の前にレコード単位でフィルタリングします。

version: 2
input:
  format: json
  json: {}
record_when:
  gt: ["@input.score", 10]
mappings:
  - target: "id"
    source: "id"
# score が 10 より大きいレコードのみ変換される

複合条件

record_when:
  all:
    - gte: ["@input.age", 18]
    - match: ["@input.email", "@company\\.com$"]
# 18歳以上かつ社内メールアドレスのレコードのみ

注意: record_when では @input.* と @context.* のみ参照可能です。@out.* は参照できません。


when 条件分岐

when オプションを使うと、条件に応じてマッピングをスキップできます。

- target: "premiumBadge"
  value: true
  when:
    eq: ["@input.tier", "premium"]
- target: "adultVerified"
  value: true
  when:
    all:
      - gte: ["@input.age", 18]
      - eq: ["@input.verified", true]

注意: when が false または評価エラーの場合、そのマッピングはスキップされます。


演算子詳細

文字列操作

concat

パイプ値と引数を文字列連結します。

expr:
  - "Hello, "
  - concat: ["@out.name"]

入力: {"name": "Alice"} (nameは先に処理済み)
出力: "Hello, Alice"

to_string

値を文字列に変換します。

expr:
  - "@input.age"
  - to_string

入力: {"age": 25}
出力: "25"

trim

前後の空白を削除します。

expr:
  - "@input.name"
  - trim

入力: {"name": " 田中 "}
出力: "田中"

lowercase

小文字に変換します。

expr:
  - "@input.code"
  - lowercase

入力: {"code": "ABC"}
出力: "abc"

uppercase

大文字に変換します。

expr:
  - "@input.code"
  - uppercase

入力: {"code": "abc"}
出力: "ABC"

replace

文字列を置換します。3番目の引数で置換モードを指定できます。

# デフォルト(先頭一致のみ)
expr:
  - "@input.text"
  - replace: ["abc", "XYZ"]
# "abc-123-abc" -> "XYZ-123-abc"

# all(全置換)
expr:
  - "@input.text"
  - replace: ["abc", "XYZ", "all"]
# "abc-123-abc" -> "XYZ-123-XYZ"

# regex(正規表現、先頭一致)
expr:
  - "@input.text"
  - replace: ["\\d+", "NUM", "regex"]
# "abc-123-456" -> "abc-NUM-456"

# regex_all(正規表現、全置換)
expr:
  - "@input.text"
  - replace: ["\\d", "_", "regex_all"]
# "abc-123" -> "abc-___"
split

区切り文字で分割して配列を返します。

expr:
  - "@input.tags"
  - split: [","]

入力: {"tags": "a,b,c"}
出力: ["a", "b", "c"]

pad_start

指定長になるまで先頭を埋めます。

expr:
  - "@input.code"
  - pad_start: [5, "0"]

入力: {"code": "42"}
出力: "00042"

pad_end

指定長になるまで末尾を埋めます。

expr:
  - "@input.name"
  - pad_end: [10, "_"]

入力: {"name": "ABC"}
出力: "ABC_______"


数値演算

加算
expr:
  - "@input.a"
  - +: ["@input.b", "@input.c"]

入力: {"a": 1, "b": 2, "c": 3}
出力: 6

減算
expr:
  - "@input.total"
  - -: [4]

入力: {"total": 10}
出力: 6

乗算
expr:
  - "@input.price"
  - multiply: [1.1]

入力: {"price": 100}
出力: 110.0

除算
expr:
  - "@input.total"
  - /: [2]

入力: {"total": 9}
出力: 4.5

round

四捨五入します。引数で小数桁数を指定できます。

# 整数に丸める
expr:
  - "@input.value"
  - round
# 12.5 -> 13

# 小数第2位まで
expr:
  - "@input.value"
  - round: [2]
# 12.345 -> 12.35
to_base

整数をN進数の文字列に変換します(2-36進数対応)。

# 16進数変換
expr:
  - "@input.color"
  - to_base: [16]
# 255 -> "ff"

# 2進数変換
expr:
  - 10
  - to_base: [2]
# -> "1010"
複合計算例(華氏→摂氏変換)
expr:
  - "@input.fahrenheit"
  - -: [32]
  - multiply: [5]
  - /: [9]
  - round: [2]

入力: {"fahrenheit": 98.6}
出力: 37.0


日付処理

date_format

日時文字列を別のフォーマットに変換します。

# 基本
expr:
  - "@input.date"
  - date_format: ["%Y/%m/%d"]
# "2024-01-02" -> "2024/01/02"

# タイムゾーン指定
expr:
  - "@input.datetime"
  - date_format: ["%Y-%m-%d %H:%M:%S", "UTC", "+09:00"]

対応フォーマット:

  • ISO 8601: 2024-01-02T15:04:05Z
  • RFC 2822/3339
  • YYYY-MM-DD / YYYY/MM/DD
to_unixtime

日時文字列をUnix時間に変換します。

# 秒単位
expr:
  - "1970-01-01T00:00:01Z"
  - to_unixtime
# -> 1

# ミリ秒単位
expr:
  - "@input.timestamp"
  - to_unixtime: ["ms"]

論理演算

and
expr:
  - "@input.verified"
  - and: ["@input.active"]
or
expr:
  - "@input.is_admin"
  - or: ["@input.is_owner"]
not
expr:
  - "@input.disabled"
  - not

入力: {"disabled": false}
出力: true


比較演算

注意: 条件構文(when, record_when, if.cond)では eq, ne, gt などの専用形式を使用してください。

等価比較
expr:
  - "@input.status"
  - ==: ["active"]
数値比較
expr:
  - "@input.score"
  - >=: [60]
# 75 -> true
正規表現マッチ
expr:
  - "@input.email"
  - ~=: [".+@example\\.com$"]
# "test@example.com" -> true

検索・フォールバック

coalesce

最初の「missing でも null でもない」値を返します。

expr:
  - "@input.nickname"
  - coalesce: ["@input.name", "unknown"]
lookup

配列を検索し、マッチした要素を 配列 で返します。

# from を明示
expr:
  - lookup:
    - "@context.tags"
    - "id"
    - "@input.tag_id"
    - "value"

# パイプ値を配列として使用
expr:
  - "@context.tags"
  - lookup:
    - "id"
    - "@input.tag_id"
    - "value"
lookup_first

配列を検索し、最初にマッチした要素を返します。

expr:
  - lookup_first:
    - "@context.departments"
    - id
    - "@input.dept_id"
    - name

入力: {"dept_id": 1}, コンテキスト: {"departments": [{"id": 1, "name": "営業"}]}
出力: "営業"


JSON操作

merge

複数のオブジェクトをシャローマージします(後勝ち)。

expr:
  - "@input.base"
  - merge: ["@input.override"]
# {"a": 1, "b": 2} + {"b": 3, "c": 4} -> {"a": 1, "b": 3, "c": 4}
deep_merge

ネストしたオブジェクトも再帰的にマージします。

expr:
  - "@input.base"
  - deep_merge: ["@input.override"]
# {"a": {"x": 1}} + {"a": {"y": 2}} -> {"a": {"x": 1, "y": 2}}
get

ドットパスで値を取得します。

expr:
  - "@input.obj"
  - get: ["user.profile.name"]
pick

指定パスのみを抽出した新しいオブジェクトを返します。

expr:
  - "@input.obj"
  - pick: ["id", "profile.name"]
omit

指定パスを除外した新しいオブジェクトを返します。

expr:
  - "@input.obj"
  - omit: ["password", "internal.secret"]
keys

オブジェクトのキーを配列で返します。

expr:
  - "@input.map"
  - keys
# {"a": 1, "b": 2} -> ["a", "b"]
values

オブジェクトの値を配列で返します。

expr:
  - "@input.map"
  - values
# {"a": 1, "b": 2} -> [1, 2]
entries

オブジェクトを {key, value} の配列に変換します。

expr:
  - "@input.map"
  - entries
# {"a": 1, "b": 2} -> [{"key": "a", "value": 1}, {"key": "b", "value": 2}]
len

文字列/配列/オブジェクトの長さを返します。

# 配列の長さ
expr:
  - "@input.array"
  - len
# [1, 2, 3] -> 3

# 文字列の長さ(Unicode文字単位)
expr:
  - "@input.text"
  - len
# "こんにちは" -> 5
from_entries

エントリからオブジェクトを構築します。

# [key, value]形式の配列
expr:
  - "@input.pairs"
  - from_entries
# [["a", 1], ["b", 2]] -> {"a": 1, "b": 2}

# 2引数: キーと値を直接指定
expr:
  - from_entries: ["status", "@input.code"]
# {"code": 200} -> {"status": 200}
object_flatten

ネストしたオブジェクトをドットパスキーでフラット化します。

expr:
  - "@input.obj"
  - object_flatten
# {"user": {"profile": {"age": 30}}} -> {"user.profile.age": 30}
object_unflatten

ドットパスキーのオブジェクトをネスト構造に復元します。

expr:
  - "@input.flat"
  - object_unflatten
# {"user.name": "田中"} -> {"user": {"name": "田中"}}

配列操作(変換)

map

各要素に対してパイプを適用し、結果の配列を返します。

expr:
  - "@input.numbers"
  - map:
    - multiply: [2]
# [1, 2, 3] -> [2.0, 4.0, 6.0]
filter

条件に一致する要素のみを含む配列を返します。

expr:
  - "@input.numbers"
  - filter:
    - >: [2]
# [1, 2, 3, 4] -> [3, 4]
flat_map

各要素を変換後、結果をフラット化します。

expr:
  - "@input.nested"
  - flat_map:
    - "@item"
# [[1, 2], [3, 4]] -> [1, 2, 3, 4]
flatten

配列を指定の深さまでフラット化します。

# 1階層(デフォルト)
expr:
  - "@input.nested"
  - flatten
# [[1, 2], [3, 4]] -> [1, 2, 3, 4]

# 2階層
expr:
  - "@input.nested"
  - flatten: [2]

配列操作(抽出)

first

配列の先頭要素を返します。

expr:
  - "@input.items"
  - first
# [1, 2, 3] -> 1
last

配列の末尾要素を返します。

expr:
  - "@input.items"
  - last
# [1, 2, 3] -> 3
take

先頭または末尾からN件取得します。

# 先頭2件
expr:
  - "@input.items"
  - take: [2]
# [1, 2, 3, 4, 5] -> [1, 2]

# 末尾2件(負数)
expr:
  - "@input.items"
  - take: [-2]
# [1, 2, 3, 4, 5] -> [4, 5]
drop

先頭または末尾からN件除外します。

# 先頭2件を除外
expr:
  - "@input.items"
  - drop: [2]
# [1, 2, 3, 4, 5] -> [3, 4, 5]
slice

範囲指定で要素を取得します(end は排他)。

expr:
  - "@input.items"
  - slice: [1, -1]
# [1, 2, 3, 4, 5] -> [2, 3, 4](インデックス1から末尾-1まで)
chunk

配列をN件ずつの配列に分割します。

expr:
  - "@input.items"
  - chunk: [2]
# [1, 2, 3, 4, 5] -> [[1, 2], [3, 4], [5]]

配列操作(結合)

zip

複数の配列をペアの配列に結合します。

expr:
  - "@input.keys"
  - zip: ["@input.values"]
# keys: ["a", "b"], values: [1, 2] -> [["a", 1], ["b", 2]]
zip_with

複数の配列を結合関数で処理します。

expr:
  - "@input.nums"
  - zip_with:
    - "@input.strs"
    - concat: ["@item[1]"]
# nums: [1, 2], strs: ["a", "b"] -> ["1a", "2b"]
unzip

ペア配列を複数の配列に分離します。

expr:
  - "@input.pairs"
  - unzip
# [["a", 1], ["b", 2]] -> [["a", "b"], [1, 2]]

配列操作(グループ化)

group_by

キー式でグループ化し、オブジェクトを返します。

expr:
  - "@input.items"
  - group_by:
    - "@item.category"
key_by

キー式でインデックス化し、オブジェクトを返します(重複時は後勝ち)。

expr:
  - "@input.items"
  - key_by:
    - "@item.id"
partition

条件で配列を2分割します。[true配列, false配列] を返します。

expr:
  - "@input.numbers"
  - partition:
    - >: [2]
# [1, 2, 3, 4] -> [[3, 4], [1, 2]]

配列操作(重複処理)

unique

配列から重複を除去します(順序保持)。

expr:
  - "@input.tags"
  - unique
# ["a", "b", "a", "c", "b"] -> ["a", "b", "c"]
distinct_by

キー式基準で重複を除去します(最初の要素を保持)。

expr:
  - "@input.items"
  - distinct_by:
    - "@item.id"

配列操作(ソート・検索)

sort_by

キー式基準で昇順ソートします。

expr:
  - "@input.items"
  - sort_by:
    - "@item.score"
find

条件に一致する最初の要素を返します(見つからなければ null)。

expr:
  - "@input.items"
  - find:
    - >: [2]
# [1, 2, 3, 4] -> 3
find_index

条件に一致する最初の要素のインデックスを返します(見つからなければ -1)。

expr:
  - "@input.items"
  - find_index:
    - >: [2]
# [1, 2, 3, 4] -> 2
index_of

値のインデックスを返します(見つからなければ -1)。

expr:
  - "@input.items"
  - index_of: [3]
# [1, 2, 3, 4] -> 2
contains

配列に値が含まれるかを判定します。

expr:
  - "@input.tags"
  - contains: ["premium"]
# ["basic", "premium"] -> true

配列操作(集計)

sum

数値配列の合計を返します。

expr:
  - "@input.numbers"
  - sum
# [1, 2, 3, 4] -> 10
avg

数値配列の平均を返します。

expr:
  - "@input.numbers"
  - avg
# [1, 2, 3, 4] -> 2.5
min

数値配列の最小値を返します。

expr:
  - "@input.numbers"
  - min
# [3, 1, 4, 1, 5] -> 1
max

数値配列の最大値を返します。

expr:
  - "@input.numbers"
  - max
# [3, 1, 4, 1, 5] -> 5
reduce

配列を畳み込みます。初期値は最初の要素です。

expr:
  - "@input.numbers"
  - reduce:
    - +: ["@item"]
# [1, 2, 3, 4] -> 10
fold

初期値を指定して配列を畳み込みます。

expr:
  - "@input.numbers"
  - fold:
    - 100
    - +: ["@item"]
# [1, 2, 3, 4] -> 110

型変換

string

値を文字列に変換します。

expr:
  - "@input.id"
  - string
int

値を整数に変換します。

expr:
  - "@input.count"
  - int
float

値を浮動小数点に変換します。

expr:
  - "@input.value"
  - float
bool

値を真偽値に変換します。

expr:
  - "@input.flag"
  - bool

DTO自動生成

rulemorph はルールファイルから7言語のDTO定義を自動生成できます。

対応言語

  • Rust
  • TypeScript
  • Python
  • Go
  • Java
  • Kotlin
  • Swift

TypeScript 出力例

ルールファイル:

version: 2
input:
  format: json
  json: {}
output:
  name: "User"
mappings:
  - target: "id"
    source: "id"
    type: "string"
    required: true
  - target: "profile.name"
    source: "name"
  - target: "profile.age"
    source: "age"
    type: "int"
  - target: "active"
    source: "active"
    type: "bool"
    required: true

生成されるTypeScript:

export interface User {
  id: string;
  profile?: UserProfile;
  active: boolean;
}

export interface UserProfile {
  name?: string;
  age?: number;
}

required: true のフィールドは必須プロパティに、それ以外はオプショナル(?)になります。


完成例

基本: 文字列加工とパイプ

version: 2
input:
  format: json
  json: {}
mappings:
  - target: name
    expr:
      - "@input.name"
      - trim
      - uppercase
  - target: greeting
    expr:
      - "Hello, "
      - concat: ["@out.name"]
  - target: price
    expr:
      - "@input.base_price"
      - multiply: ["@context.exchange_rate"]
  - target: currency
    value: "USD"

入力: [{"name": " alice ", "base_price": 100}]
コンテキスト: {"exchange_rate": 1.5}
出力:

[{
  "name": "ALICE",
  "greeting": "Hello, ALICE",
  "price": 150.0,
  "currency": "USD"
}]

let/if/map ステップ

version: 2
input:
  format: json
  json: {}
mappings:
  - target: total_with_tax
    expr:
      - "@input.price"
      - let: { base: "$" }
      - multiply: [1.1]
  - target: discount_applied
    expr:
      - "@input.price"
      - if:
          cond:
            gt: ["$", 100]
          then: ["$", multiply: [0.9]]
          else: ["$"]
  - target: doubled_items
    expr:
      - "@input.items"
      - map:
        - multiply: [2]
  - target: tier
    expr:
      - "@input.price"
      - let: { p: "$" }
      - if:
          cond:
            gte: ["@p", 200]
          then: ["gold"]
          else:
            - if:
                cond:
                  gte: ["@p", 100]
                then: ["silver"]
                else: ["bronze"]

入力: [{"price": 150, "items": [10, 20]}]
出力:

[{
  "total_with_tax": 165.0,
  "discount_applied": 135.0,
  "doubled_items": [20.0, 40.0],
  "tier": "silver"
}]

条件構文

version: 2
input:
  format: json
  json: {}
mappings:
  - target: is_premium_active
    expr:
      - "@input.tier"
      - if:
          cond:
            all:
              - eq: ["$", "premium"]
              - eq: ["@input.status", "active"]
          then: [true]
          else: [false]
  - target: is_email
    expr:
      - "@input.contact"
      - if:
          cond:
            match: ["$", ".*@.*"]
          then: [true]
          else: [false]

lookup によるマスターデータ結合

version: 2
input:
  format: json
  json: {}
mappings:
  - target: dept_name
    expr:
      - lookup_first:
        - "@context.departments"
        - id
        - "@input.dept_id"
        - name
  - target: all_projects
    expr:
      - lookup:
        - "@context.projects"
        - owner_id
        - "@input.id"
        - name

入力: [{"id": 1, "dept_id": 10}]
コンテキスト:

{
  "departments": [{"id": 10, "name": "営業"}],
  "projects": [
    {"owner_id": 1, "name": "Project A"},
    {"owner_id": 1, "name": "Project B"}
  ]
}

出力:

[{
  "dept_name": "営業",
  "all_projects": ["Project A", "Project B"]
}]

map 内での let 使用

version: 2
input:
  format: json
  json: {}
mappings:
  - target: results
    expr:
      - "@input.items"
      - map:
        - let: { doubled: ["$", multiply: [2]] }
        - "@doubled"

入力: [{"items": [1, 2, 3]}]
出力: [{"results": [2.0, 4.0, 6.0]}]


トラブルシューティング

よくあるエラーと対処法

エラー 原因 対処法
missing required field required=true で値が missing または null default を追加、または required=false に変更
type cast failed 型変換不可(例: "abc" を int に変換) 入力値を確認、型指定を見直し
forward out reference 未定義の @out.* を参照 マッピングの順序を確認(上から順に評価)
invalid reference 不正な参照形式 @ 付きの名前空間を確認
unknown operation 未知のオペレーション名 オペレーション名のスペルを確認
division by zero 除算の分母が 0 入力値を確認

デバッグのヒント

  1. 段階的に構築: 複雑なパイプは一度に書かず、シンプルなステップから始めて徐々に追加
  2. missing vs null: 意図しない null は coalesce でフォールバック
  3. 型変換エラー: 入力データの型を確認、必要なら string で文字列化してから処理
  4. out 参照: マッピングの順序を確認、参照先が先に定義されているか確認
  5. 条件構文: when/if.cond では eq, gt などの専用形式を使用

関連リンク

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?