JMeter JSR223后置处理器:JSON数据处理与格式转换实战指南

概述

在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数据处理场景提供了参考模板。在实际应用中,可以根据具体需求调整脚本,如添加数据验证、过滤、排序等功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值