Skip to content

Commit 5e54e60

Browse files
authored
Merge pull request #259 from Integration-Automation/feat/jsonpath-batch
Add JSONPath querying over parsed JSON
2 parents db60bd9 + b8e1be7 commit 5e54e60

15 files changed

Lines changed: 485 additions & 0 deletions

File tree

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
## Table of Contents
1515

16+
- [What's new (2026-06-20) — JSONPath Querying](#whats-new-2026-06-20--jsonpath-querying)
1617
- [What's new (2026-06-20) — Multi-Channel Webhook Notifications](#whats-new-2026-06-20--multi-channel-webhook-notifications)
1718
- [What's new (2026-06-20) — Outbound CloudEvents Emitter](#whats-new-2026-06-20--outbound-cloudevents-emitter)
1819
- [What's new (2026-06-20) — Environment-Scoped Typed Asset Store](#whats-new-2026-06-20--environment-scoped-typed-asset-store)
@@ -103,6 +104,12 @@
103104

104105
---
105106

107+
## What's new (2026-06-20) — JSONPath Querying
108+
109+
Query API/DB JSON with wildcards, recursion, filters. Full reference: [`docs/source/Eng/doc/new_features/v51_features_doc.rst`](docs/source/Eng/doc/new_features/v51_features_doc.rst).
110+
111+
- **`json_query` / `json_query_one` / `json_extract`** (`AC_json_query` / `AC_json_extract`, `ac_*`): the executor's path walker only split on `.` and indexed — this adds a JSONPath subset (`$`, `.key`, `[n]`/`[-n]`, `*`/`[*]`, `..` recursive descent, `[?(@.k op v)]` filters) over parsed JSON, so array-bearing API/DB responses are easy to extract from. `json_extract` runs a `{key: path}` mapping into a flat dict. Pure-stdlib `re`; the path engine `AC_http_to_var` and DB-row flows were missing.
112+
106113
## What's new (2026-06-20) — Multi-Channel Webhook Notifications
107114

108115
Alert Teams/Discord/Slack/webhook. Full reference: [`docs/source/Eng/doc/new_features/v50_features_doc.rst`](docs/source/Eng/doc/new_features/v50_features_doc.rst).

README/README_zh-CN.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
## 目录
1414

15+
- [本次更新 (2026-06-20) — JSONPath 查询](#本次更新-2026-06-20--jsonpath-查询)
1516
- [本次更新 (2026-06-20) — 多通道 Webhook 通知](#本次更新-2026-06-20--多通道-webhook-通知)
1617
- [本次更新 (2026-06-20) — 对外 CloudEvents 发送器](#本次更新-2026-06-20--对外-cloudevents-发送器)
1718
- [本次更新 (2026-06-20) — 环境范围的带类型资产存储](#本次更新-2026-06-20--环境范围的带类型资产存储)
@@ -102,6 +103,12 @@
102103

103104
---
104105

106+
## 本次更新 (2026-06-20) — JSONPath 查询
107+
108+
以通配符、递归、过滤查询 API/DB JSON。完整参考:[`docs/source/Zh/doc/new_features/v51_features_doc.rst`](../docs/source/Zh/doc/new_features/v51_features_doc.rst)
109+
110+
- **`json_query` / `json_query_one` / `json_extract`**(`AC_json_query` / `AC_json_extract``ac_*`):执行器的路径遍历只会以 `.` 切分并索引 —— 本功能在已解析 JSON 上加入 JSONPath 子集(`$``.key``[n]`/`[-n]``*`/`[*]``..` 递归下降、`[?(@.k op v)]` 过滤),让含数组的 API/DB 响应易于提取。`json_extract``{key: path}` 映射提取成扁平 dict。纯标准库 `re`;这是 `AC_http_to_var` 与 DB-row 流程所缺的路径引擎。
111+
105112
## 本次更新 (2026-06-20) — 多通道 Webhook 通知
106113

107114
通知 Teams/Discord/Slack/webhook。完整参考:[`docs/source/Zh/doc/new_features/v50_features_doc.rst`](../docs/source/Zh/doc/new_features/v50_features_doc.rst)

README/README_zh-TW.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
## 目錄
1414

15+
- [本次更新 (2026-06-20) — JSONPath 查詢](#本次更新-2026-06-20--jsonpath-查詢)
1516
- [本次更新 (2026-06-20) — 多通道 Webhook 通知](#本次更新-2026-06-20--多通道-webhook-通知)
1617
- [本次更新 (2026-06-20) — 對外 CloudEvents 發送器](#本次更新-2026-06-20--對外-cloudevents-發送器)
1718
- [本次更新 (2026-06-20) — 環境範圍的具型別資產儲存](#本次更新-2026-06-20--環境範圍的具型別資產儲存)
@@ -102,6 +103,12 @@
102103

103104
---
104105

106+
## 本次更新 (2026-06-20) — JSONPath 查詢
107+
108+
以萬用字元、遞迴、過濾查詢 API/DB JSON。完整參考:[`docs/source/Zh/doc/new_features/v51_features_doc.rst`](../docs/source/Zh/doc/new_features/v51_features_doc.rst)
109+
110+
- **`json_query` / `json_query_one` / `json_extract`**(`AC_json_query` / `AC_json_extract``ac_*`):執行器的路徑走訪只會以 `.` 切分並索引 —— 本功能在已解析 JSON 上加入 JSONPath 子集(`$``.key``[n]`/`[-n]``*`/`[*]``..` 遞迴下降、`[?(@.k op v)]` 過濾),讓含陣列的 API/DB 回應易於擷取。`json_extract``{key: path}` 對應擷取成扁平 dict。純標準函式庫 `re`;這是 `AC_http_to_var` 與 DB-row 流程所缺的路徑引擎。
111+
105112
## 本次更新 (2026-06-20) — 多通道 Webhook 通知
106113

107114
通知 Teams/Discord/Slack/webhook。完整參考:[`docs/source/Zh/doc/new_features/v50_features_doc.rst`](../docs/source/Zh/doc/new_features/v50_features_doc.rst)
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
JSONPath Querying
2+
=================
3+
4+
The executor's built-in path walker only splits on ``.`` and indexes — it can't
5+
do wildcards, recursive descent, or filters, so API/DB responses with arrays are
6+
awkward to extract from. ``json_query`` adds a focused JSONPath subset over
7+
already-parsed JSON:
8+
9+
================ ===================================================
10+
Syntax Meaning
11+
================ ===================================================
12+
``$`` root (optional prefix)
13+
``.name`` member access
14+
``[n]`` ``[-n]`` list index (negative from the end)
15+
``*`` ``[*]`` wildcard (all members / elements)
16+
``..`` recursive descent
17+
``[?(@.k op v)]`` filter array elements (``op`` ∈ ``== != < <= > >=``)
18+
================ ===================================================
19+
20+
Pure standard library (``re``); imports no ``PySide6``.
21+
22+
Headless API
23+
------------
24+
25+
.. code-block:: python
26+
27+
from je_auto_control import json_query, json_query_one, json_extract
28+
29+
json_query(data, "$.store.books[*].title") # every title
30+
json_query(data, "$.store.books[?(@.price > 8)].title") # filtered
31+
json_query(data, "$..price") # recursive descent
32+
33+
json_query_one(data, "$.user.name", default="?") # first match or default
34+
json_extract(data, {"name": "$.user.name", # mapping -> flat dict
35+
"first_tag": "$.tags[0]"})
36+
37+
``json_query`` returns **all** matches (a list); ``json_query_one`` returns the
38+
first (or a default); ``json_extract`` runs a ``{key: path}`` mapping into a flat
39+
dict (first match per path). This is the path engine the existing
40+
``AC_http_to_var`` / API / DB-row flows were missing.
41+
42+
Executor commands
43+
-----------------
44+
45+
================================ ===================================================
46+
Command Effect
47+
================================ ===================================================
48+
``AC_json_query`` ``{matches}`` — all values matching a JSONPath.
49+
``AC_json_extract`` ``{result}`` — a ``{key: path}`` mapping extracted.
50+
================================ ===================================================
51+
52+
``data`` (and ``mapping``) accept a JSON object or a JSON string (so the visual
53+
builder works). The same operations are exposed as MCP tools (``ac_json_query`` /
54+
``ac_json_extract``) and as Script Builder commands under **Data**.

docs/source/Eng/eng_index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ Comprehensive guides for all AutoControl features.
7373
doc/new_features/v48_features_doc
7474
doc/new_features/v49_features_doc
7575
doc/new_features/v50_features_doc
76+
doc/new_features/v51_features_doc
7677
doc/ocr_backends/ocr_backends_doc
7778
doc/observability/observability_doc
7879
doc/operations_layer/operations_layer_doc
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
JSONPath 查詢
2+
=============
3+
4+
執行器內建的路徑走訪只會以 ``.`` 切分並索引 —— 無法做萬用字元、遞迴下降或過濾,因此含
5+
陣列的 API/DB 回應很難擷取。``json_query`` 在已解析的 JSON 上加入聚焦的 JSONPath 子集:
6+
7+
================ ===================================================
8+
語法 意義
9+
================ ===================================================
10+
``$`` 根(可省略前綴)
11+
``.name`` 成員存取
12+
``[n]`` ``[-n]`` 串列索引(負數由尾端起算)
13+
``*`` ``[*]`` 萬用字元(所有成員 / 元素)
14+
``..`` 遞迴下降
15+
``[?(@.k op v)]`` 過濾陣列元素(``op`` ∈ ``== != < <= > >=``)
16+
================ ===================================================
17+
18+
純標準函式庫(``re``);不匯入 ``PySide6``。
19+
20+
無頭 API
21+
--------
22+
23+
.. code-block:: python
24+
25+
from je_auto_control import json_query, json_query_one, json_extract
26+
27+
json_query(data, "$.store.books[*].title") # 每個 title
28+
json_query(data, "$.store.books[?(@.price > 8)].title") # 過濾
29+
json_query(data, "$..price") # 遞迴下降
30+
31+
json_query_one(data, "$.user.name", default="?") # 第一個或預設
32+
json_extract(data, {"name": "$.user.name", # 對應 -> 扁平 dict
33+
"first_tag": "$.tags[0]"})
34+
35+
``json_query`` 回傳**所有**符合項(清單);``json_query_one`` 回傳第一個(或預設);
36+
``json_extract`` 以 ``{key: path}`` 對應擷取成扁平 dict(每路徑取第一個符合項)。這正是
37+
既有 ``AC_http_to_var`` / API / DB-row 流程所缺的路徑引擎。
38+
39+
執行器指令
40+
----------
41+
42+
================================ ===================================================
43+
指令 效果
44+
================================ ===================================================
45+
``AC_json_query`` ``{matches}`` —— 符合 JSONPath 的所有值。
46+
``AC_json_extract`` ``{result}`` —— 擷取 ``{key: path}`` 對應。
47+
================================ ===================================================
48+
49+
``data``(與 ``mapping``)接受 JSON 物件或 JSON 字串(因此視覺化建構器可用)。相同操作亦
50+
提供為 MCP 工具(``ac_json_query`` / ``ac_json_extract``),以及 Script Builder 中 **Data**
51+
分類下的指令。

docs/source/Zh/zh_index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ AutoControl 所有功能的完整使用指南。
7373
doc/new_features/v48_features_doc
7474
doc/new_features/v49_features_doc
7575
doc/new_features/v50_features_doc
76+
doc/new_features/v51_features_doc
7677
doc/ocr_backends/ocr_backends_doc
7778
doc/observability/observability_doc
7879
doc/operations_layer/operations_layer_doc

je_auto_control/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,10 @@
276276
from je_auto_control.utils.notify_channels import (
277277
WebhookChannel, WebhookResult, notify_webhook, set_default_poster,
278278
)
279+
# JSONPath-style querying over parsed JSON
280+
from je_auto_control.utils.jsonpath import (
281+
json_extract, json_query, json_query_one,
282+
)
279283
# Background popup/interrupt watchdog (unattended automation)
280284
from je_auto_control.utils.watchdog import (
281285
PopupWatchdog, WatchdogRule, default_popup_watchdog,
@@ -738,6 +742,7 @@ def start_autocontrol_gui(*args, **kwargs):
738742
"Asset", "AssetStore", "AssetValue", "active_environment",
739743
"EventEmitter", "post_cloudevent", "to_cloudevent",
740744
"WebhookChannel", "WebhookResult", "notify_webhook", "set_default_poster",
745+
"json_extract", "json_query", "json_query_one",
741746
# MCP server
742747
"AuditLogger", "HttpMCPServer", "MCPContent", "MCPPrompt",
743748
"MCPPromptArgument", "MCPResource", "MCPServer", "MCPTool",

je_auto_control/gui/script_builder/command_schema.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1127,6 +1127,24 @@ def _add_misc_specs(specs: List[CommandSpec]) -> None:
11271127
),
11281128
description="Send a Slack/Discord/Teams/raw webhook notification.",
11291129
))
1130+
specs.append(CommandSpec(
1131+
"AC_json_query", "Data", "JSONPath: Query",
1132+
fields=(
1133+
FieldSpec("data", FieldType.STRING,
1134+
placeholder='{"a": [1, 2]}'),
1135+
FieldSpec("path", FieldType.STRING, placeholder="$.a[*]"),
1136+
),
1137+
description="Query parsed JSON with a JSONPath subset (all matches).",
1138+
))
1139+
specs.append(CommandSpec(
1140+
"AC_json_extract", "Data", "JSONPath: Extract Mapping",
1141+
fields=(
1142+
FieldSpec("data", FieldType.STRING),
1143+
FieldSpec("mapping", FieldType.STRING,
1144+
placeholder='{"name": "$.user.name"}'),
1145+
),
1146+
description="Extract a {key: jsonpath} mapping into a flat object.",
1147+
))
11301148
specs.append(CommandSpec(
11311149
"AC_generate_sop", "Report", "Generate SOP Document",
11321150
fields=(

je_auto_control/utils/executor/action_executor.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3296,6 +3296,26 @@ def _notify_webhook(url: str, text: str, transport: str = "raw",
32963296
"transport": outcome.transport}
32973297

32983298

3299+
def _json_query(data: Any, path: str) -> Dict[str, Any]:
3300+
"""Adapter: return all JSONPath matches in data (JSON string or object)."""
3301+
import json
3302+
from je_auto_control.utils.jsonpath import json_query
3303+
if isinstance(data, str):
3304+
data = json.loads(data)
3305+
return {"matches": json_query(data, path)}
3306+
3307+
3308+
def _json_extract(data: Any, mapping: Any) -> Dict[str, Any]:
3309+
"""Adapter: extract a {key: path} mapping from data into a flat dict."""
3310+
import json
3311+
from je_auto_control.utils.jsonpath import json_extract
3312+
if isinstance(data, str):
3313+
data = json.loads(data)
3314+
if isinstance(mapping, str):
3315+
mapping = json.loads(mapping)
3316+
return {"result": json_extract(data, mapping)}
3317+
3318+
32993319
class Executor:
33003320
"""
33013321
Executor
@@ -3576,6 +3596,8 @@ def __init__(self):
35763596
"AC_list_assets": _list_assets,
35773597
"AC_emit_event": _emit_event,
35783598
"AC_notify_webhook": _notify_webhook,
3599+
"AC_json_query": _json_query,
3600+
"AC_json_extract": _json_extract,
35793601
"AC_a11y_record_start": _a11y_record_start,
35803602
"AC_a11y_record_stop": _a11y_record_stop,
35813603
"AC_a11y_record_events": _a11y_record_events,

0 commit comments

Comments
 (0)