乌海市网站建设_网站建设公司_外包开发_seo优化
2026/1/1 2:23:01 网站建设 项目流程

概述

在JMeter性能测试中,我们经常需要处理复杂的JSON数据格式转换。本文通过一个实际案例,详细介绍如何使用JSR223后置处理器对提取的JSON数据进行格式转换,解决中文字符编码问题,并生成符合目标接口要求的数据格式。

问题场景

原始数据结构

我们通过JSON提取器获取了凭证数据,数据结构如下:

json

voucherData_ALL = [ [ // 凭证1 - 多条分录 {"vouGuid": "guid1", "amtDrYsCN": "零", ...}, {"vouGuid": "guid1", "amtDrYsCN": "零", ...} ], [ // 凭证2 - 多条分录 {"vouGuid": "guid2", "amtDrYsCN": "零", ...} ], // ... 更多凭证 ]

目标数据结构

需要转换为以下格式,供下一个接口使用:

json

[ { "gl_voucher_ds1": [ // 原始凭证分录数组 {"vouGuid": "guid1", "amtDrYsCN": "零", ...}, {"vouGuid": "guid1", "amtDrYsCN": "零", ...} ], "lp_bill_info": [] // 空数组 }, { "gl_voucher_ds1": [ {"vouGuid": "guid2", "amtDrYsCN": "零", ...} ], "lp_bill_info": [] } ]

技术挑战

  1. 数据结构转换:从二维数组转换为对象数组

  2. 中文字符编码:防止JSON序列化时将中文字符转为Unicode编码

  3. 性能考虑:在JMeter中高效处理大量数据

解决方案

基础脚本实现

groovy

import groovy.json.JsonOutput import groovy.json.JsonSlurper // 获取提取的原始数据 def voucherDataRaw = vars.get("voucherData_ALL") // 如果数据为空,直接返回 if (voucherDataRaw == null || voucherDataRaw == "null") { log.error("voucherData_ALL 变量为空或未找到") return } try { // 解析JSON数据 def jsonSlurper = new JsonSlurper() def voucherData = jsonSlurper.parseText(voucherDataRaw) // 转换数据格式 def transformedData = [] // 遍历每个凭证 voucherData.each { voucher -> // 创建一个新的对象 def newVoucher = [ "gl_voucher_ds1": voucher, "lp_bill_info": [] ] // 添加到结果数组 transformedData.add(newVoucher) } // 将转换后的数据转换为JSON字符串 def jsonOutput = JsonOutput.toJson(transformedData) // 打印调试信息 log.info("转换成功,共处理了 " + transformedData.size() + " 个凭证") // 将结果存储到变量中,供下一个请求使用 vars.put("voucherData_Transformed", jsonOutput) } catch (Exception e) { log.error("处理凭证数据时发生错误: " + e.getMessage()) log.error("Stack trace: ", e) }

解决中文字符编码问题

上述基础脚本存在一个问题:当使用JsonOutput.toJson()方法时,中文字符会被自动转义为Unicode编码(如:"零"变为"\u96f6")。以下是解决方案:

groovy

import groovy.json.JsonOutput import groovy.json.JsonSlurper // 获取提取的原始数据 def voucherDataRaw = vars.get("voucherData_ALL") if (voucherDataRaw == null || voucherDataRaw == "null") { log.error("voucherData_ALL 变量为空或未找到") return } try { // 解析JSON数据 def jsonSlurper = new JsonSlurper() def voucherData = jsonSlurper.parseText(voucherDataRaw) // 转换数据格式 def transformedData = [] // 遍历每个凭证 voucherData.each { voucher -> // 创建一个新的对象 def newVoucher = [ "gl_voucher_ds1": voucher, "lp_bill_info": [] ] // 添加到结果数组 transformedData.add(newVoucher) } // 将转换后的数据转换为JSON字符串 def jsonOutput = JsonOutput.toJson(transformedData) // 解码Unicode转义字符,将\uXXXX格式转回中文字符 jsonOutput = jsonOutput.replaceAll(/\\u([0-9a-fA-F]{4})/) { // 将十六进制字符串转换为字符 char c = (char) Integer.parseInt(it[1], 16) return c.toString() } // 打印调试信息 log.info("转换成功,共处理了 " + transformedData.size() + " 个凭证") // 将结果存储到变量中,供下一个请求使用 vars.put("voucherData_Transformed", jsonOutput) } catch (Exception e) { log.error("处理凭证数据时发生错误: " + e.getMessage()) log.error("Stack trace: ", e) }

代码详解

1. 数据获取与解析

groovy

// 从JMeter变量获取数据 def voucherDataRaw = vars.get("voucherData_ALL") // 使用JsonSlurper解析JSON字符串 def jsonSlurper = new JsonSlurper() def voucherData = jsonSlurper.parseText(voucherDataRaw)
  • vars.get(): 获取JMeter变量

  • JsonSlurper: Groovy的JSON解析器,将JSON字符串转换为Groovy对象

2. 数据结构转换

groovy

def transformedData = [] voucherData.each { voucher -> def newVoucher = [ "gl_voucher_ds1": voucher, "lp_bill_info": [] ] transformedData.add(newVoucher) }
  • 遍历原始二维数组的每个元素(凭证)

  • 为每个凭证创建新对象,包含两个字段

  • 将新对象添加到结果数组

3. Unicode编码处理

groovy

jsonOutput = jsonOutput.replaceAll(/\\u([0-9a-fA-F]{4})/) { char c = (char) Integer.parseInt(it[1], 16) return c.toString() }
  • 使用正则表达式匹配Unicode转义序列(格式:\uXXXX

  • 将十六进制字符串转换为整数

  • 将整数转换为对应的Unicode字符

  • 用原字符替换Unicode转义序列

4. 结果存储与调试

groovy

// 存储结果供后续请求使用 vars.put("voucherData_Transformed", jsonOutput) // 调试信息 log.info("转换成功,共处理了 " + transformedData.size() + " 个凭证")

性能优化建议

1. 数据量较大时的优化

groovy

// 使用StringBuilder提高字符串处理性能 def result = new StringBuilder() def jsonOutput = JsonOutput.toJson(transformedData) // 手动处理Unicode解码 def i = 0 while (i < jsonOutput.length()) { if (i <= jsonOutput.length() - 6 && jsonOutput.charAt(i) == '\\' && jsonOutput.charAt(i + 1) == 'u') { def hex = jsonOutput.substring(i + 2, i + 6) try { char c = (char) Integer.parseInt(hex, 16) result.append(c) i += 6 } catch (NumberFormatException e) { result.append(jsonOutput.charAt(i)) i++ } } else { result.append(jsonOutput.charAt(i)) i++ } } vars.put("voucherData_Transformed", result.toString())

2. 内存使用优化

groovy

// 如果数据量非常大,考虑分批处理 def batchSize = 100 def batches = [] def currentBatch = [] voucherData.eachWithIndex { voucher, index -> def newVoucher = [ "gl_voucher_ds1": voucher, "lp_bill_info": [] ] currentBatch.add(newVoucher) if (currentBatch.size() >= batchSize || index == voucherData.size() - 1) { // 处理当前批次 def batchJson = JsonOutput.toJson(currentBatch) batchJson = batchJson.replaceAll(/\\u([0-9a-fA-F]{4})/) { char c = (char) Integer.parseInt(it[1], 16) return c.toString() } batches.add(batchJson) currentBatch = [] } } // 根据实际需求处理批次数据 // 可以将结果存储到多个变量或文件中

使用JMeter的最佳实践

1. 正确配置JSR223处理器

  • 语言选择: 选择"groovy"

  • 缓存编译脚本: 勾选以提高性能

  • 脚本位置: 放在JSON提取器之后,目标请求之前

2. 错误处理与日志记录

groovy

// 详细的错误处理 catch (Exception e) { log.error("处理凭证数据时发生错误: " + e.getMessage()) log.error("错误类型: " + e.getClass().getName()) // 记录原始数据用于调试 if (voucherDataRaw) { log.error("原始数据前500字符: " + voucherDataRaw.substring(0, Math.min(500, voucherDataRaw.length()))) } // 设置默认值或标记错误 vars.put("voucherData_Transformed", "[]") SampleResult.setSuccessful(false) }

3. 调试技巧

groovy

// 添加调试信息 log.info("原始数据类型: " + voucherData.getClass()) log.info("原始数据大小: " + voucherData.size()) // 检查转换结果 if (transformedData && transformedData[0].gl_voucher_ds1) { def sampleEntry = transformedData[0].gl_voucher_ds1[0] log.info("示例数据: amtDrYsCN = " + sampleEntry.amtDrYsCN + ", vouDesc = " + sampleEntry.vouDesc) } // 将结果写入文件(调试用) def debugFile = new File("/tmp/jmeter_debug_" + System.currentTimeMillis() + ".json") debugFile.write(jsonOutput) log.info("调试文件已保存: " + debugFile.absolutePath)

常见问题与解决方案

问题1: 数据为空

现象:voucherData_ALL变量为空
解决: 检查JSON提取器的配置,确保路径正确

问题2: 中文字符仍为Unicode

现象: 转换后中文字符显示为\uXXXX格式
解决: 确保Unicode解码代码正确执行,检查正则表达式匹配

问题3: 性能问题

现象: 处理大量数据时响应缓慢
解决:

  • 启用JSR223的"缓存编译脚本"

  • 使用StringBuilder代替字符串拼接

  • 考虑分批处理

问题4: JSON格式错误

现象: 解析JSON时抛出异常
解决:

  • 检查原始数据格式

  • 添加try-catch块进行错误处理

  • 使用JSON验证工具检查数据

完整示例

以下是完整的、经过优化的脚本:

groovy

import groovy.json.JsonOutput import groovy.json.JsonSlurper try { // 1. 获取原始数据 def voucherDataRaw = vars.get("voucherData_ALL") if (!voucherDataRaw || voucherDataRaw == "null") { log.warn("voucherData_ALL 为空,使用空数组") vars.put("voucherData_Transformed", "[]") return } // 2. 解析JSON def jsonSlurper = new JsonSlurper() def voucherData = jsonSlurper.parseText(voucherDataRaw) if (!voucherData || !(voucherData instanceof List)) { log.error("数据格式错误,期望List类型") vars.put("voucherData_Transformed", "[]") return } // 3. 转换数据结构 def transformedData = [] voucherData.each { voucher -> transformedData.add([ "gl_voucher_ds1": voucher, "lp_bill_info": [] ]) } // 4. 生成JSON并处理Unicode编码 def jsonOutput = JsonOutput.toJson(transformedData) // 解码Unicode转义字符 if (jsonOutput.contains("\\u")) { jsonOutput = jsonOutput.replaceAll(/\\u([0-9a-fA-F]{4})/) { try { return (char) Integer.parseInt(it[1], 16) as String } catch (Exception e) { return it[0] // 如果转换失败,返回原字符串 } } } // 5. 存储结果 vars.put("voucherData_Transformed", jsonOutput) // 6. 记录处理结果 log.info("数据处理完成: 转换了 " + transformedData.size() + " 个凭证") } catch (Exception e) { log.error("数据处理失败: " + e.getMessage()) e.printStackTrace() // 确保有默认值 vars.put("voucherData_Transformed", "[]") }

总结

通过本文介绍的JSR223后置处理器脚本,我们可以高效地处理JMeter中的JSON数据格式转换问题。关键点包括:

  1. 正确解析JSON数据:使用Groovy的JsonSlurper

  2. 数据结构转换:按照目标格式重组数据

  3. 中文字符处理:解码Unicode转义序列

  4. 错误处理:确保脚本健壮性

  5. 性能优化:使用高效的数据处理方法

这个方案不仅解决了当前的数据转换需求,也为处理其他类似的JSON数据处理场景提供了参考模板。在实际应用中,可以根据具体需求调整脚本,如添加数据验证、过滤、排序等功能。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询