如何在KQL/Grafana中循环遍历查询提取JSON内目标子串
Grafana 嵌套链接JSON字段解析为表格列的实现方案
场景说明
Grafana表格直接展示原始长嵌套JSON字符串没有实际业务价值,当前需要处理数千条返回结果,从每条结果携带的链接数组JSON中提取两类信息:
- 每个链接对象
url字段末尾的数字工作项ID - 每个链接对象
attributes.name字段的关联类型值
最终实现效果为:以关联类型值作为表格列名,对应关联的工作项ID作为列值,结构化展示在表格中。
示例待解析原始数据如下:
[ { "rel": "System.LinkTypes.Hierarchy-Forward", "url": "https://fakelink/workItems/247155", "attributes": { "isLocked": false, "name": "Child" } }, { "rel": "System.LinkTypes.Hierarchy-Forward", "url": "https://fakelink/workItems/247154 ", "attributes": { "isLocked": false, "name": "Child" } }, { "rel": "System.LinkTypes.Hierarchy-Forward", "url": "https://fakelink/247160", "attributes": { "isLocked": false, "name": "Child" } }, { "rel": "System.LinkTypes.Hierarchy-Forward", "url": "https://fakelink/247156", "attributes": { "isLocked": false, "name": "Child" } }, { "rel": "System.LinkTypes.Hierarchy-Forward", "url": "https://fakelink/workItems/247159", "attributes": { "isLocked": false, "name": "Child" } }, { "rel": "System.LinkTypes.Hierarchy-Forward", "url": "https://fakelink/workItems/247157", "attributes": { "isLocked": false, "name": "Child" } }, { "rel": "System.LinkTypes.Hierarchy-Forward", "url": "https://fakelink/247158", "attributes": { "isLocked": false, "name": "Child" } } ]
可选实现方案
方案1:数据源侧预处理(优先选,性能最优)
如果用的是支持JSON处理的数据源(PostgreSQL、MySQL、Elasticsearch、Azure DevOps等),直接在查询阶段完成字段拆分,不要把原始长字符串丢给Grafana前端处理,几千条数据走前端解析很容易出现渲染卡顿。
核心处理逻辑:
- 先将存储JSON的字段反序列化为结构化数组
- 遍历数组中每个链接对象:
- 读取
attributes.name作为分组键,也就是后续的表格列名 - 提取url末尾的数字ID:直接用正则
/(\d+)\s*$/匹配url结尾的连续数字即可,不需要写死匹配/workItems/路径——样例里近一半的url没有这个路径段,这个正则还能自动兼容url末尾带多余空格的脏数据(比如样例里247154那条结尾的空格) - 同一条主记录下,相同关联类型的多个ID按展示需求聚合,比如用逗号、换行符拼接成单个字符串
- 读取
以PostgreSQL为例,查询阶段的解析代码参考:
SELECT record_id, -- 按链接类型聚合ID为键值对 jsonb_object_agg( link_item->'attributes'->>'name', array_agg(regexp_replace(link_item->>'url', '^.*?(\d+)\s*$', '\1', 'g')) ) AS parsed_link_map FROM your_data_table, -- 展开JSON数组为逐行的链接对象 LATERAL jsonb_array_elements(link_json_field::jsonb) AS link_item GROUP BY record_id, link_json_field
方案2:Grafana前端转换(数据源不支持JSON解析时使用)
Grafana 8.0及以上版本可以在面板的Transform栏添加「Add field from calculation」或自定义JavaScript转换,直接在前端完成字段拆分,不需要改查询逻辑。
转换核心逻辑:
- 遍历查询返回的每一行主记录
- 对每行存储原始JSON的字段做
JSON.parse()转成对象数组,加空值兜底避免解析失败 - 初始化空对象存储解析后的列键值对
- 循环遍历每个链接对象,提取关联类型名和对应的ID,同类型多ID用分隔符拼接
- 将解析出的键值对合并到原行数据,删除冗余的原始长JSON字段即可
可直接复用的转换代码片段:
const parsedResult = rawData.map(row => { // 把workItemLinks替换成你自己存储原始JSON的字段名 let linkList = []; try { linkList = JSON.parse(row.workItemLinks || '[]'); } catch (e) { // 解析失败直接返回空,避免阻塞整表渲染 linkList = []; } const parsedCols = {}; linkList.forEach(link => { const colName = link?.attributes?.name || 'unknown_type'; const matchRes = link?.url?.match(/(\d+)\s*$/); const workItemId = matchRes ? matchRes[1] : null; if (!workItemId) return; // 同类型多ID用逗号拼接,需要换行的话把逗号改成\n即可 parsedCols[colName] = parsedCols[colName] ? `${parsedCols[colName]}, ${workItemId}` : workItemId; }); // 移除原始JSON字段,合并解析后的列 const { workItemLinks, ...baseFields } = row; return { ...baseFields, ...parsedCols }; }); return parsedResult;
避坑提示
- 一定要加异常兜底:JSON格式损坏、url不符合规则、name字段为空这类脏数据在几千条的量级里基本必然存在,不做判断很容易导致整个面板渲染白屏
- 数据量超过1000条优先选数据源侧预处理,前端JS解析+渲染的速度会比数据源预处理慢一个数量级,数据量大的时候面板加载要等好几秒
- 如果后续需要支持点击ID跳转到对应工作项页面,直接在Grafana的字段覆盖里给对应ID列加数据链接即可,不需要在解析阶段拼跳转地址
内容的提问来源于stack exchange,提问作者Hudson Carpenter
相关产品推荐
相关产品推荐

