Jmeter书中不会教你的(11)——beanshell中常用的数据类型转换

        在写脚本时经常会遇到数据类型转换的问题,我自己也是记性不好,尤其是在beanshell中,每次字符串转整型,整型转字符串都要去翻看要以前记录的案例。

        在测试计划中创建一个变量cnt,值为0。在线程组下创建一个beanshell sampler

Thread Group中设置loop count=10,假设希望在日志中打印出执行csdnJ11这个取样器的次数,我们利用变量cnt,每循环一次次数加1方式,将新的值再次存入变量,关于如何存取变量在(1)和(2)中已经有详细描述。Jmeter书中不会教你的(1)——beanshell用来获取变量和设置变量的vars_agrapea的博客-CSDN博客

Jmeter书中不会教你的(2)——vars.get和${}引用区别_agrapea的博客-CSDN博客

在beanshell sampler中按如下执行后,会看到本期望b打印的是整数1,实际却是“01”,很显然在做b=a+1时并没有计算,而是做了一个0和1的字符串连接,原因是通过vars.get获取的变量值是字符串类型,所以a是字符串类型。

//字符串转整形
a=vars.get(
<think>我们已知在JMeter中,JDBC Request会返回多条数据,并存储为变量:如变量前缀为"result",则第一行的列值存储在result_1,第二行在result_2,以此类推。 现在,用户希望使用BeanShell后置处理器,通过HashMap来处理这些多条记录,并将它们转换为一个字符串(可能是JSON字符串,或者用分隔符连接的字符串)。 根据引用[3]中的示例,我们可以借鉴其思路:使用HashMap和ArrayList来构建一个复杂的数据结构,然后转换字符串(如JSON)。但用户的需求可能更简单,比如只是将每行数据连接起来。 然而,用户明确提到“通过HashMap将多条数据转换字符串”,这里我们可以有两种理解: 1. 将每行数据作为一个元素,放入HashMap(但HashMap是键值对,每行数据可能有多列,所以每行可以是一个HashMap,然后整个结果集可以是一个List<HashMap>,最后将这个List转换字符串)。 2. 或者,用户可能只是想将每行数据(可能只有一列)连接成一个字符串,但要求使用HashMap来处理(这种需求不太常见,因为连接字符串不需要HashMap)。 考虑到引用[3]中的例子,它实际上是将数据库的多行数据(每行有多列)转换成了一个JSON数组,其中每个元素是一个对象(即每行数据对应一个对象)。所以,我们假设用户需要的是类似的功能:将多行数据转换为一个结构化的字符串(如JSON),或者用分隔符连接每行中的某一列(或全部列)的值。 由于用户问题中提到了“转换字符串”,并且参考了引用[3]的复杂结构,我们将提供两种方案: 方案一:将每行数据(假设只有一列)连接成一个用分隔符分隔的字符串(简单连接)。 方案二:将每行数据(可能有多列)转换为一个JSON数组,每个元素是一个对象(复杂结构,如引用[3])。 但是,用户要求使用HashMap。在方案一中,使用HashMap并不是必须的,所以我们重点参考方案二(使用HashMap来构建每行数据,然后放入List,最后为JSON字符串)。 然而,请注意:在JDBC Request中,如果查询返回多列,那么变量会按照列名和行号生成,例如: 列名为name和age,则第一行:result_1, result_2(但这样不对,应该是name_1, age_1) 实际上,JMeter的JDBC Request对于多列的处理是:每个列都会有一个变量,变量名为“变量前缀_列名_行号”(注意:默认情况下,变量名中的列名是大写的,除非在查询中使用了别名)。 因此,我们需要明确: - 查询返回了几列? - 用户希望如何组织这些数据? 由于用户没有明确,我们将按照通用的方式处理:假设查询返回多列,我们将每行数据放入一个HashMap(键为列名,值为该列的值),然后将所有行放入一个List,最后将这个List转换为JSON字符串(这样就是数组形式的JSON)。 步骤: 1. 在JDBC Request中设置变量前缀,例如:result。 2. 在BeanShell PostProcessor中编写脚本,遍历所有行,对于每一行,遍历所有列,将列名和对应的值放入一个HashMap。 3. 将每行的HashMap添加到一个ArrayList中。 4. 使用JSON库(如fastjson,需要导入jar包)将ArrayList转换为JSON字符串,或者使用简单的字符串拼接(不推荐,复杂结构用JSON更合适)。 但是,BeanShell内置支持Java语法,但默认没有JSON库。因此,我们可以: - 使用JMeter自带的json.org包(但需要导入,且BeanShell中需要写全类名)或者 - 使用字符串拼接(对于简单情况)或者 - 导入外部JSON库(如fastjson,需要将jar包放入JMeter的lib目录,并重启JMeter)。 考虑到引用[3]中使用了fastjson,我们假设用户已经将fastjson的jar包放入了JMeter的lib目录。 下面,我们按照使用fastjson的方式编写脚本。 注意:由于用户可能不知道如何导入jar包,我们也可以避免使用外部库,使用字符串拼接成JSON格式(但这样容易出错,特别是数据中有特殊字符时)。因此,我们推荐使用fastjson。 脚本思路: 1. 获取变量前缀(假设为"result")。 2. 获取列名列表(可以通过一个已知列名的列表,或者通过获取第一行的列名来推断?但是BeanShell中如何知道列名?) 实际上,在JDBC Request中,列名可以通过变量名中的列名部分来获取,但是我们需要知道返回了哪些列。 问题:在BeanShell中如何知道返回了哪些列? - 方法1:在JDBC Request中,我们可以设置“variable names”属性(一个逗号分隔的列名列表)。这样,JMeter会为每个列名生成变量,并且还会生成一个列名列表的变量(变量名为:变量前缀+“_#”)。 - 方法2:如果没有设置“variable names”,那么JMeter会使用查询结果中的列名(大写)作为变量名的一部分。 我们假设用户已经在JDBC Request中设置了“variable names”为“col1,col2”(或者用户知道列名)。这样,我们可以通过获取变量前缀+"_#"的值来获取列的数量(这个变量存储了列的数量),同时,变量前缀+"_1"对应第一行第一列,变量前缀+"_2"对应第一行第二列,等等。但是,注意:设置“variable names”后,变量名会变成:变量前缀_列别名_行号?实际上,JMeter的文档说明: - 如果没有设置“variable names”,那么变量名为:<变量前缀>_<列号>(从1开始),以及一个额外的变量<变量前缀>_#存储列数。 - 如果设置了“variable names”,那么变量名为:<变量前缀>_<给定的列名>_<行号>(行号从1开始),同时还会生成一个变量<变量前缀>_<给定的列名>_#存储该列的行数。 因此,为了明确列名,我们建议在JDBC Request中设置“variable names”。 假设我们在JDBC Request中: - Variable Name: result - Variable names: name,age 那么,第一行数据:name列的值在变量result_name_1,age列的值在result_age_1;第二行:result_name_2, result_age_2,以此类推。 同时,会生成两个变量:result_name_#(值为行数)和result_age_#(值为行数)。 所以,我们可以通过任意一个列名的行数变量(如result_name_#)来获取总行数。 脚本步骤(BeanShell): 1. 定义变量前缀和列名数组。 2. 获取行数:通过vars.get("result_name_#"),注意这里需要根据一个列名来获取。 3. 遍历每一行(从1到行数)。 4. 对于每一行,遍历每一列,根据列名和行号构建变量名,获取该列的值,并放入HashMap(键为列名,值为变量值)。 5. 将该行的HashMap添加到一个ArrayList中。 6. 使用fastjson将ArrayList转换为JSON字符串,并保存到JMeter变量。 注意:BeanShell脚本中,使用vars.get()获取变量值,vars.put()保存变量。 参考引用[3]的代码,我们稍作修改: 假设列名是固定的(已知为"name","age"),那么: ```java import java.util.*; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; // 定义变量前缀和列名 String varPrefix = "result"; String[] columnNames = {"name", "age"}; // 替换为实际的列名 // 获取行数(通过第一列的行数变量) String rowCountVar = varPrefix + "_" + columnNames[0] + "_#"; int rowCount = Integer.parseInt(vars.get(rowCountVar)); // 注意:如果没有行,可能为null,需要处理 // 创建一个List来保存所有行的数据 List<Map<String, String>> allRows = new ArrayList<Map<String, String>>(); // 遍历每一行 for (int row = 1; row <= rowCount; row++) { // 创建一个Map来保存当前行的数据 Map<String, String> rowMap = new HashMap<String, String>(); // 遍历每一列 for (int col = 0; col < columnNames.length; col++) { String colName = columnNames[col]; // 构建变量名:varPrefix + "_" + colName + "_" + row String varName = varPrefix + "_" + colName + "_" + row; String value = vars.get(varName); // 将列名和值放入Map rowMap.put(colName, value); } allRows.add(rowMap); } // 将List转换为JSON字符串 String jsonString = JSONArray.toJSONString(allRows); vars.put("jsonResult", jsonString); // 打印到日志(调试用) log.info("JSON Result: " + jsonString); ``` 但是,如果用户没有设置“variable names”,那么变量名会是:result_1, result_2, ...(每列一个序列,而不是按列名)。这种情况下,我们无法知道每列对应的列名(除非我们知道列的顺序和列名)。所以,强烈建议设置“variable names”。 如果用户没有设置,那么我们可以通过列号来获取,但需要知道列的数量(通过变量result_#获取列数)和列名(如果不知道列名,就无法给HashMap赋值键)。所以,这种情况需要用户事先知道列的顺序和对应的列名。 假设用户知道列名,并且知道列的数量(假设为2列),列名为["col1", "col2"],那么变量名规则为: - 第一行:result_1(第一列), result_2(第二列) - 第二行:result_3, result_4 此时,我们可以这样: ```java import java.util.*; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; String varPrefix = "result"; String[] columnNames = {"col1", "col2"}; // 假设的列名 int columnCount = columnNames.length; // 获取总行数:通过变量 result_# 可以获取列数?不对,这里result_#是列的数量,不是行数。 // 实际上,行数需要计算:总变量数 = 行数 * 列数。但是,JMeter会生成一个变量存储行数吗?不会。 // 在没有设置“variable names”的情况下,JMeter会生成: // result_# = 列数 // 以及每个单元格的变量:result_1, result_2, ... 一直到 result_N(N=行数*列数) // 另外,还会生成一个变量:result_#=行数?不对,根据文档,会生成一个变量存储行数:变量前缀+".rowcount"(但这是JMeter 4.0以后?)或者通过result_#只能知道列数。 // 实际上,在JDBC Request中,无论是否设置“variable names”,都会生成一个变量:<变量前缀>_rowcount 来存储行数(注意:这个变量名是JMeter 5.0的,早期版本可能是<变量前缀>.rowcount?) // 根据官方文档:https://jmeter.apache.org/usermanual/component_reference.html#JDBC_Request // - 如果没有设置“variable names”,那么会生成: // <variable name>_# = 列数 // <variable name>_<column number> = 值(列号从1开始) // <variable name>_rowcount = 行数 // - 如果设置了“variable names”,那么会生成: // <variable name>_<column name>_# = 行数(每个列都会有一个) // <variable name>_<column name>_<row number> = 值 // <variable name>_rowcount = 行数 // 因此,我们可以使用变量:<变量前缀>_rowcount 来获取行数。 int rowCount = Integer.parseInt(vars.get(varPrefix + "_rowcount")); List<Map<String, String>> allRows = new ArrayList<Map<String, String>>(); // 遍历每一行 for (int row = 1; row <= rowCount; row++) { Map<String, String> rowMap = new HashMap<String, String>(); for (int col = 1; col <= columnCount; col++) { // 变量名:varPrefix + "_" + ( (row-1)*columnCount + col ) // 因为第一行:第一列是1,第二列是2;第二行:第一列是3,第二列是4... int varIndex = (row - 1) * columnCount + col; String varName = varPrefix + "_" + varIndex; String value = vars.get(varName); // 列名用我们定义的列名数组(注意:col-1是因为数组从0开始) String colName = columnNames[col-1]; rowMap.put(colName, value); } allRows.add(rowMap); } String jsonString = JSONArray.toJSONString(allRows); vars.put("jsonResult", jsonString); ``` 但是,这种方式需要用户事先知道列的顺序和列名,而且容易出错(因为变量索引的计算)。所以,我们推荐在JDBC Request中设置“variable names”。 综上所述,我们给出两种情况的代码,但优先推荐设置“variable names”的方式。 另外,如果用户只需要将每行的一列(比如只有一列)连接成字符串(用逗号分隔),那么我们可以简化(不使用HashMap和JSON): ```java String varPrefix = "result"; String separator = ","; // 获取行数:通过变量result_rowcount int rowCount = Integer.parseInt(vars.get(varPrefix + "_rowcount")); StringBuilder sb = new StringBuilder(); // 注意:如果只有一列,那么变量名就是:result_1, result_2, ... result_rowcount for (int i = 1; i <= rowCount; i++) { String value = vars.get(varPrefix + "_" + i); if (i > 1) { sb.append(separator); } sb.append(value); } vars.put("combinedString", sb.toString()); ``` 但是,用户要求使用HashMap。所以,如果用户希望用HashMap来存储每行数据(即使只有一列),那么我们可以将每行数据作为一个HashMap(键为列名,值为该值),然后转换字符串。但这样转换出来的字符串并不是简单的连接,而是JSON格式。 因此,请根据用户的实际需求选择。 由于用户问题中要求使用HashMap,我们提供第一种方案(设置“variable names”并转换为JSON数组)的完整代码。 注意:使用fastjson需要导入jar包(如fastjson-1.2.78.jar)到JMeter的lib目录,并重启JMeter。 如果用户不想使用外部jar包,我们可以使用字符串拼接成JSON格式(但需要处理义): - 对于简单的数据(不包含引号、换行等),可以手动拼接。 手动拼接JSON的示例(不推荐,仅作备用): ```java String varPrefix = "result"; String[] columnNames = {"name", "age"}; String rowCountVar = varPrefix + "_" + columnNames[0] + "_#"; int rowCount = Integer.parseInt(vars.get(rowCountVar)); StringBuilder jsonSB = new StringBuilder(); jsonSB.append("["); for (int row = 1; row <= rowCount; row++) { if (row > 1) { jsonSB.append(","); } jsonSB.append("{"); for (int col = 0; col < columnNames.length; col++) { if (col > 0) { jsonSB.append(","); } String colName = columnNames[col]; String varName = varPrefix + "_" + colName + "_" + row; String value = vars.get(varName); // 对value进行JSON义(这里简单处理,如果包含双引号,需要义) value = value.replace("\"", "\\\""); jsonSB.append("\"").append(colName).append("\":\"").append(value).append("\""); } jsonSB.append("}"); } jsonSB.append("]"); vars.put("jsonResult", jsonSB.toString()); ``` 但是,手动拼接JSON容易出错,特别是当数据包含特殊字符时。所以,我们还是推荐使用fastjson。 总结步骤: 1. 在JDBC Request中设置Variable names(逗号分隔的列名)。 2. 将fastjson的jar包放入JMeter的lib目录,重启JMeter。 3. 添加BeanShell PostProcessor,编写上述第一种脚本(使用fastjson)。 最后,将生成的JSON字符串(变量名为jsonResult)用于后续请求。 注意:如果用户不需要JSON格式,而是希望用其他方式(如用分号连接每行,每行内用冒号分隔键值对),可以根据需求修改。 由于用户问题中要求使用HashMap,我们提供了将每行数据放入HashMap,然后构建List并为JSON字符串的方法。这满足了使用HashMap处理并转换字符串的需求。 如果用户有进一步的需求,可以根据实际情况调整。 请根据实际情况选择代码并调整。</think>### JMeter BeanShell 使用 HashMap 处理数据库多条记录并转换字符串JMeter 中,通过 JDBC Request 获取数据库返回的多条记录后,可以使用 **BeanShell PostProcessor** 结合 HashMap 数据结构将多条数据转换字符串(通常是 JSON 格式)。以下是详细步骤和实现方法: --- #### 步骤 1:配置 JDBC Request 1. 在 JDBC Request 中设置 **Variable Name**(如 `dbResult`) 2. 确保查询返回多条记录(如 `SELECT name, age FROM users`) 3. JMeter 会自动生成变量: - `dbResult_#`:总行数 - `dbResult_1` / `dbResult_2`:第一行数据(列值用逗号分隔) - `dbResult_1_name` / `dbResult_1_age`:第一行的具体列值(需开启 *Variable names*) --- #### 步骤 2:添加 BeanShell PostProcessor 在 JDBC Request 下添加 **BeanShell PostProcessor**,使用以下脚本: ```java import java.util.HashMap; import java.util.Map; import java.util.ArrayList; import com.alibaba.fastjson.JSONObject; // 需添加fastjson库 // 1. 获取查询结果总行数 int rowCount = Integer.parseInt(vars.get("dbResult_#")); ArrayList<Map> resultList = new ArrayList(); // 2. 遍历每一行记录 for (int i = 1; i <= rowCount; i++) { // 创建HashMap存储单行数据 Map<String, String> rowMap = new HashMap(); // 3. 获取当前行的列值 String name = vars.get("dbResult_" + i + "_name"); String age = vars.get("dbResult_" + i + "_age"); // 4. 放入HashMap rowMap.put("name", name); rowMap.put("age", age); // 5. 添加到结果列表 resultList.add(rowMap); } // 6. 转换为JSON字符串 String jsonResult = JSONObject.toJSONString(resultList); vars.put("jsonResult", jsonResult); // 调试输出(可选) log.info("JSON Result: " + jsonResult); ``` --- #### 关键配置说明 1. **变量命名规则**: - 总行数 → `${dbResult_#}` - 第N行第M列 → `${dbResult_N_colNameM}` 2. **FastJSON 依赖**: - 下载 [fastjson-1.2.78.jar](https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.78/fastjson-1.2.78.jar) - 放入 JMeter 的 `lib` 目录并重启 3. **HashMap 处理逻辑**: ```mermaid graph LR A[获取总行数] --> B[遍历每一行] B --> C[创建HashMap] C --> D[填充列数据] D --> E[添加到ArrayList] E --> F[转换为JSON字符串] ``` --- #### 使用转换结果 转换后的字符串存储在 `${jsonResult}` 变量中,格式如下: ```json [ {"name":"Alice", "age":"25"}, {"name":"Bob", "age":"30"}, {"name":"Charlie", "age":"28"} ] ``` 可在后续请求中通过 `${jsonResult}` 引用该值。 --- #### 注意事项 1. **列名大小写**: - JMeter 默认生成大写的列名变量(如 `dbResult_1_NAME`) - 如需小写,在 SQL 中使用别名:`SELECT name AS username` 2. **空值处理**: ```java String name = vars.get("dbResult_" + i + "_name") != null ? vars.get("dbResult_" + i + "_name") : "N/A"; ``` 3. **性能优化**: - 对于超 1000 行的数据集,建议分批处理 - 在 JDBC Request 中设置合理的 **Fetch Size** 4. **替代方案**(无 JSON 库时): ```java StringBuilder sb = new StringBuilder("["); for (int i = 1; i <= rowCount; i++) { sb.append("{"); sb.append("\"name\":\"").append(vars.get("dbResult_"+i+"_name")).append("\","); sb.append("\"age\":").append(vars.get("dbResult_"+i+"_age")); sb.append(i < rowCount ? "}," : "}"); } sb.append("]"); vars.put("jsonResult", sb.toString()); ``` > 此方法通过 HashMap 实现数据结构化,再转换为 JSON 字符串,特别适用于 API 测试中需要传递结构化参数的场景[^3]。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

agrapea

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值