本文介绍在 jmeter 中使用 jsr223 postprocessor(groovy)动态遍历任意嵌套 json 响应,无需预知字段名即可完整提取路径式键名与对应值,适用于接口返回结构不固定、需全量解析的测试场景。
本文介绍在 jmeter 中使用 jsr223 postprocessor(groovy)动态遍历任意嵌套 json 响应,无需预知字段名即可完整提取路径式键名与对应值,适用于接口返回结构不固定、需全量解析的测试场景。
在性能测试中,常遇到后端返回 JSON 结构动态变化的情况(例如设备列表中 attributes 字段包含不同厂商自定义属性),此时无法通过常规的 JSON Extractor 预设 JSONPath 表达式提取字段。JMeter 原生不支持“泛型键值遍历”,但借助 Groovy 强大的 JSON 处理能力,我们可编写递归脚本实现全自动路径发现与键值捕获。
以下是一个健壮、生产可用的 Groovy 脚本,它会深度遍历整个 JSON 树,对每个叶子节点(非 Map/Collection 的终态值) 生成形如 devices[0].attributes.name = "SmartSensor" 的键值对,并支持直接写入 JMeter 变量或日志:
import groovy.json.JsonSlurper
def traverse = { tree, result = [], prefix = '' ->
switch (tree) {
case Map:
tree.each { k, v ->
def keyName = prefix ? "${prefix}.${k}" : k
if (v instanceof Map || v instanceof Collection) {
// 继续递归:对象或数组不作为终态值输出
traverse(v, result, keyName)
} else {
// 终态值(String/Number/Boolean/Null)→ 记录键值对
result
def indexedKey = "${prefix}[${idx}]"
if (item instanceof Map || item instanceof Collection) {
traverse(item, result, indexedKey)
} else {
result
log.info("JSON Path: ${pair.key} = ${pair.value}")
}
// 【推荐】存入 JMeter 变量供后续使用(例如写入CSV或断言)
vars.put("jsonAllKeys", allKVPairs.collect{ it.key }.join("|"))
vars.put("jsonAllValues", allKVPairs.collect{ it.value }.join("|"))
vars.putObject("jsonFullMap", allKVPairs) // 保存为 List<map> 对象
} catch (Exception e) {
log.error("Failed to parse or traverse JSON: ${e.message}", e)
}</map>
✅ 关键特性说明:
- ✅ 路径精确化:自动构建带索引的路径(如 devices[24].attributes.latitude),保留数组层级语义;
- ✅ 值类型安全:调用 .toString() 统一处理 null、Boolean、Integer 等类型,避免 NullPointerException;
- ✅ 变量可扩展:结果以 List
- ✅ 错误防御:空响应、JSON 解析失败均有日志记录和降级处理。
⚠️ 注意事项:
- 该脚本需置于 JSR223 PostProcessor 中,且必须放在目标 HTTP 请求之后;
- 若 JSON 极大(>10MB),递归可能引发栈溢出或内存压力,建议配合 jmeter.properties 中 jsr223.maxSize=5000000 限制;
- 生产环境慎用 log.info() 打印全部键值(尤其含敏感字段时),建议改用 log.debug() 并开启 DEBUG 日志级别;
- 如需导出为 CSV,可在脚本末尾添加 new File("json_output.csv").withWriter { w -> allKVPairs.each { w.println("${it.key},${it.value}") } }。
掌握此方法后,你将彻底摆脱“字段未知就无法提取”的困境,让 JMeter 真正具备动态契约感知能力——无论 API 如何演进,你的测试脚本都能自适应解析。










