如何根据变量值识别 csv 文件中的一行,并从 Groovy 中的那一行开始遍历文件行?
How can I Identify a line in a csv file based on a variable value and start iterating over the file lines from that line onwards in Groovy?
我正在使用 Jmeter 向某些 API 发送多个 HTTPS 请求。我每次都需要用不同的数据组成这些请求的 XML 主体。
我有一个如下所示的 csv 文件:
iDocType,iDocId,iName,iDate
P,555551555,Braiden,2022-12-31
I,100000001,Dominique,2024-12-10
P,100000002,Joyce,2025-11-15
.........
我在 JSR223 预处理器(在 HTTPS 采样器下)中有这段代码,它创建多个 XML 对象并用 csv 值一个一个地填充它们,最后将该对象提供给 Jmeter 以用于作为请求正文数据的采样器:
def writer = new StringWriter()
def xml = new groovy.xml.MarkupBuilder(writer)
def pReqID = 'ID0000'
def numberOfNodes = 3
//input from csv file
travelDocNbr = vars.get('iDocId').toInteger();
def lines = new File("TestData\test.csv").readLines()
xml.nodes() {
1.upto(numberOfNodes, { lineNo ->
xml.object(a: 'false', b: 'false', pReqID: pReqID + (lineNo).toString()) {
xml.al(ad: '2021-09-20', alc: 'bla', bla: '2021-09-20T11:00:00.000Z', sn: 'AB8912')
xml.doc(docType: lines.get(lineNo).split(',')[0],
docId: lines.get(lineNo).split(',')[1],
name: lines.get(lineNo).split(',')[2],
date: lines.get(lineNo).split(',')[3])
}
}
)
}
def nodeAsText = writer.toString()
//log.info(nodeAsText)
vars.put('passengers3XmlObj', nodeAsText)
这总是给出这样的结果:
<nodes>
<object a='false' b='false' pReqID='ID00001'>
<al ad='2021-09-20' alc='bla' bla='2021-09-20T11:00:00.000Z' sn='AB8912' />
<doc docType='P' docId='555551555' name='Braiden' date='12/31/2022' />
</object>
<object a='false' b='false' pReqID='ID00002'>
<al ad='2021-09-20' alc='bla' bla='2021-09-20T11:00:00.000Z' sn='AB8912' />
<doc docType='P' docId='100000001' name='Dominique' date='12/10/2024' />
</object>
<object a='false' b='false' pReqID='ID00003'>
<al ad='2021-09-20' alc='bla' bla='2021-09-20T11:00:00.000Z' sn='AB8912' />
<doc docType='P' docId='100000002' name='Joyce' date='11/15/2025' />
</object>
</nodes>
问题在于此脚本从 CSV 文件中的第一个值开始组合对象,并且始终在每个采样器请求的正文中创建相同的 XML 对象。
我每次都需要它来更改 CSV 中的值。为此,我在想是否可以有一个在每次采样器迭代中始终变化的变量,例如 iName 值(由于 CSV 数据集配置测试元素,我在每次采样器迭代中得到一个不同的值)并使用它来查找CSV 文件中的行并从该行开始组合对象。
因此,例如,假设 csv 文件只有开头提到的 3 行,并且我将值“Dominique”存储在一个变量中,我想将它传递给一个函数并获得以下结果:
<nodes>
<object a='false' b='false' pReqID='ID00001'>
<al ad='2021-09-20' alc='bla' bla='2021-09-20T11:00:00.000Z' sn='AB8912' />
<doc docType='P' docId='100000001' name='Dominique' date='12/10/2024' />
</object>
<object a='false' b='false' pReqID='ID00002'>
<al ad='2021-09-20' alc='bla' bla='2021-09-20T11:00:00.000Z' sn='AB8912' />
<doc docType='P' docId='100000002' name='Joyce' date='11/15/2025' />
</object>
<object a='false' b='false' pReqID='ID00003'>
<al ad='2021-09-20' alc='bla' bla='2021-09-20T11:00:00.000Z' sn='AB8912' />
<doc docType='P' docId='555551555' name='Braiden' date='12/31/2022' />
</object>
</nodes>
谁能告诉我这是怎么做到的?
更新:
感谢@injecteer,我最终得到了这段代码,在我的实现中,它没有按预期工作:
def pReqID = 'ID0000'
def numberOfNodes = 10
String startFrom = 'Dominique'
int pos = -1
// @injecteer
def text = new File("TestData\\validTravellerDocuments.csv").text
// @injecteer
def csv = text.readLines().drop( 1 ).withIndex().collect{
def ( docType, docId, name, date ) = it[ 0 ].trim().split( ',' )
if( -1 == pos && startFrom == name ) pos = it[ 1 ]
[ docType:docType, docId:docId, name:name, date:date ]
}
//here the skipped lines are reodered to the bottom, if needed
def reoderedCsv = -1 < pos ? csv[ pos..-1 ] + csv[ 0..<pos ] : csv
log.info reoderedCsv
def writer = new StringWriter()
new groovy.xml.MarkupBuilder(writer).nodes() {
reoderedCsv.take numberOfNodes eachWithIndex{ data, ix ->
object(a: 'false', b: 'false', pReqID:"$pReqID${ix + 1}" ) {
al ad:'2021-09-20', alc:'bla', bla:'2021-09-20T11:00:00.000Z', sn:'AB8912'
doc data
}
log.info "$data -> ${data.getClass()}"
}
}
def nodesAsText = writer.toString()
log.info(nodesAsText)
当前的问题是该部分以这种方式填充:
如果我要求 10 个节点,我会得到这样的行为:
<doc>D</doc>
...
<doc>o</doc>
...
<doc>c</doc>
...
<doc>T</doc>
...
<doc>y</doc>
...
<doc>p</doc>
...
<doc>e</doc>
...
<doc>,</doc>
etc...
如果您想获得 Dominique
文本所在的行数,可以使用 findIndexOf() function 来完成,例如:
def dominiquePosition = lines.findIndexOf { line -> line.contains('Dominique') }
然后您可以从上一行中检索到的位置开始构建您的 XML:
dominiquePosition.upto(numberOfNodes, { lineNo ->
可以使用 vars.getIteration()
函数获得迭代,其中 vars
代表 JMeterVariables class instance. See Top 8 JMeter Java Classes You Should Be Using with Groovy article for more details on this and other JMeter API 可用于 JSR223 测试元素的简写
我会以自然的 groovy 方式放置代码。
更新:
我添加了更多 CSV 行来演示 numberOfNodes
如何影响输出。
def pReqID = 'ID0000'
def numberOfNodes = 2
String startFrom = 'Dominique'
int pos = -1
def csv = """\
iDocType,iDocId,iName,iDate
P,555551555,Braiden,2022-12-31
I,100000001,Dominique,2024-12-10
P,100000002,Joyce,2025-11-15
P,555551555,Braiden,2022-12-31
I,100000001,Dominique,2024-12-10
P,100000002,Joyce,2025-11-15
""".readLines().drop( 1 ).withIndex().collect{
def ( docType, docId, name, date ) = it[ 0 ].trim().split( ',' )
if( -1 == pos && startFrom == name ) pos = it[ 1 ]
[ docType:docType, docId:docId, name:name, date:date ]
}
//here the skipped lines are reodered to the bottom, if needed
def reoderedCsv = -1 < pos ? csv[ pos..-1 ] + csv[ 0..<pos ] : csv
def writer = new StringWriter()
new groovy.xml.MarkupBuilder(writer).nodes() {
reoderedCsv.take numberOfNodes eachWithIndex{ data, ix ->
object(a: 'false', b: 'false', pReqID:"$pReqID${ix + 1}" ) {
al ad:'2021-09-20', alc:'bla', bla:'2021-09-20T11:00:00.000Z', sn:'AB8912'
doc data
}
}
}
println writer
打印:
<nodes>
<object a='false' b='false' pReqID='ID00001'>
<al ad='2021-09-20' alc='bla' bla='2021-09-20T11:00:00.000Z' sn='AB8912' />
<doc docType='I' docId='100000001' name='Dominique' date='2024-12-10' />
</object>
<object a='false' b='false' pReqID='ID00002'>
<al ad='2021-09-20' alc='bla' bla='2021-09-20T11:00:00.000Z' sn='AB8912' />
<doc docType='P' docId='100000002' name='Joyce' date='2025-11-15' />
</object>
</nodes>
使用 numberOfNodes = 3
和更多代码打印您想要的输出
我正在使用 Jmeter 向某些 API 发送多个 HTTPS 请求。我每次都需要用不同的数据组成这些请求的 XML 主体。
我有一个如下所示的 csv 文件:
iDocType,iDocId,iName,iDate
P,555551555,Braiden,2022-12-31
I,100000001,Dominique,2024-12-10
P,100000002,Joyce,2025-11-15
.........
我在 JSR223 预处理器(在 HTTPS 采样器下)中有这段代码,它创建多个 XML 对象并用 csv 值一个一个地填充它们,最后将该对象提供给 Jmeter 以用于作为请求正文数据的采样器:
def writer = new StringWriter()
def xml = new groovy.xml.MarkupBuilder(writer)
def pReqID = 'ID0000'
def numberOfNodes = 3
//input from csv file
travelDocNbr = vars.get('iDocId').toInteger();
def lines = new File("TestData\test.csv").readLines()
xml.nodes() {
1.upto(numberOfNodes, { lineNo ->
xml.object(a: 'false', b: 'false', pReqID: pReqID + (lineNo).toString()) {
xml.al(ad: '2021-09-20', alc: 'bla', bla: '2021-09-20T11:00:00.000Z', sn: 'AB8912')
xml.doc(docType: lines.get(lineNo).split(',')[0],
docId: lines.get(lineNo).split(',')[1],
name: lines.get(lineNo).split(',')[2],
date: lines.get(lineNo).split(',')[3])
}
}
)
}
def nodeAsText = writer.toString()
//log.info(nodeAsText)
vars.put('passengers3XmlObj', nodeAsText)
这总是给出这样的结果:
<nodes>
<object a='false' b='false' pReqID='ID00001'>
<al ad='2021-09-20' alc='bla' bla='2021-09-20T11:00:00.000Z' sn='AB8912' />
<doc docType='P' docId='555551555' name='Braiden' date='12/31/2022' />
</object>
<object a='false' b='false' pReqID='ID00002'>
<al ad='2021-09-20' alc='bla' bla='2021-09-20T11:00:00.000Z' sn='AB8912' />
<doc docType='P' docId='100000001' name='Dominique' date='12/10/2024' />
</object>
<object a='false' b='false' pReqID='ID00003'>
<al ad='2021-09-20' alc='bla' bla='2021-09-20T11:00:00.000Z' sn='AB8912' />
<doc docType='P' docId='100000002' name='Joyce' date='11/15/2025' />
</object>
</nodes>
问题在于此脚本从 CSV 文件中的第一个值开始组合对象,并且始终在每个采样器请求的正文中创建相同的 XML 对象。 我每次都需要它来更改 CSV 中的值。为此,我在想是否可以有一个在每次采样器迭代中始终变化的变量,例如 iName 值(由于 CSV 数据集配置测试元素,我在每次采样器迭代中得到一个不同的值)并使用它来查找CSV 文件中的行并从该行开始组合对象。
因此,例如,假设 csv 文件只有开头提到的 3 行,并且我将值“Dominique”存储在一个变量中,我想将它传递给一个函数并获得以下结果:
<nodes>
<object a='false' b='false' pReqID='ID00001'>
<al ad='2021-09-20' alc='bla' bla='2021-09-20T11:00:00.000Z' sn='AB8912' />
<doc docType='P' docId='100000001' name='Dominique' date='12/10/2024' />
</object>
<object a='false' b='false' pReqID='ID00002'>
<al ad='2021-09-20' alc='bla' bla='2021-09-20T11:00:00.000Z' sn='AB8912' />
<doc docType='P' docId='100000002' name='Joyce' date='11/15/2025' />
</object>
<object a='false' b='false' pReqID='ID00003'>
<al ad='2021-09-20' alc='bla' bla='2021-09-20T11:00:00.000Z' sn='AB8912' />
<doc docType='P' docId='555551555' name='Braiden' date='12/31/2022' />
</object>
</nodes>
谁能告诉我这是怎么做到的?
更新: 感谢@injecteer,我最终得到了这段代码,在我的实现中,它没有按预期工作:
def pReqID = 'ID0000'
def numberOfNodes = 10
String startFrom = 'Dominique'
int pos = -1
// @injecteer
def text = new File("TestData\\validTravellerDocuments.csv").text
// @injecteer
def csv = text.readLines().drop( 1 ).withIndex().collect{
def ( docType, docId, name, date ) = it[ 0 ].trim().split( ',' )
if( -1 == pos && startFrom == name ) pos = it[ 1 ]
[ docType:docType, docId:docId, name:name, date:date ]
}
//here the skipped lines are reodered to the bottom, if needed
def reoderedCsv = -1 < pos ? csv[ pos..-1 ] + csv[ 0..<pos ] : csv
log.info reoderedCsv
def writer = new StringWriter()
new groovy.xml.MarkupBuilder(writer).nodes() {
reoderedCsv.take numberOfNodes eachWithIndex{ data, ix ->
object(a: 'false', b: 'false', pReqID:"$pReqID${ix + 1}" ) {
al ad:'2021-09-20', alc:'bla', bla:'2021-09-20T11:00:00.000Z', sn:'AB8912'
doc data
}
log.info "$data -> ${data.getClass()}"
}
}
def nodesAsText = writer.toString()
log.info(nodesAsText)
当前的问题是该部分以这种方式填充: 如果我要求 10 个节点,我会得到这样的行为:
<doc>D</doc>
...
<doc>o</doc>
...
<doc>c</doc>
...
<doc>T</doc>
...
<doc>y</doc>
...
<doc>p</doc>
...
<doc>e</doc>
...
<doc>,</doc>
etc...
如果您想获得 Dominique
文本所在的行数,可以使用 findIndexOf() function 来完成,例如:
def dominiquePosition = lines.findIndexOf { line -> line.contains('Dominique') }
然后您可以从上一行中检索到的位置开始构建您的 XML:
dominiquePosition.upto(numberOfNodes, { lineNo ->
可以使用 vars.getIteration()
函数获得迭代,其中 vars
代表 JMeterVariables class instance. See Top 8 JMeter Java Classes You Should Be Using with Groovy article for more details on this and other JMeter API 可用于 JSR223 测试元素的简写
我会以自然的 groovy 方式放置代码。
更新:
我添加了更多 CSV 行来演示 numberOfNodes
如何影响输出。
def pReqID = 'ID0000'
def numberOfNodes = 2
String startFrom = 'Dominique'
int pos = -1
def csv = """\
iDocType,iDocId,iName,iDate
P,555551555,Braiden,2022-12-31
I,100000001,Dominique,2024-12-10
P,100000002,Joyce,2025-11-15
P,555551555,Braiden,2022-12-31
I,100000001,Dominique,2024-12-10
P,100000002,Joyce,2025-11-15
""".readLines().drop( 1 ).withIndex().collect{
def ( docType, docId, name, date ) = it[ 0 ].trim().split( ',' )
if( -1 == pos && startFrom == name ) pos = it[ 1 ]
[ docType:docType, docId:docId, name:name, date:date ]
}
//here the skipped lines are reodered to the bottom, if needed
def reoderedCsv = -1 < pos ? csv[ pos..-1 ] + csv[ 0..<pos ] : csv
def writer = new StringWriter()
new groovy.xml.MarkupBuilder(writer).nodes() {
reoderedCsv.take numberOfNodes eachWithIndex{ data, ix ->
object(a: 'false', b: 'false', pReqID:"$pReqID${ix + 1}" ) {
al ad:'2021-09-20', alc:'bla', bla:'2021-09-20T11:00:00.000Z', sn:'AB8912'
doc data
}
}
}
println writer
打印:
<nodes>
<object a='false' b='false' pReqID='ID00001'>
<al ad='2021-09-20' alc='bla' bla='2021-09-20T11:00:00.000Z' sn='AB8912' />
<doc docType='I' docId='100000001' name='Dominique' date='2024-12-10' />
</object>
<object a='false' b='false' pReqID='ID00002'>
<al ad='2021-09-20' alc='bla' bla='2021-09-20T11:00:00.000Z' sn='AB8912' />
<doc docType='P' docId='100000002' name='Joyce' date='2025-11-15' />
</object>
</nodes>
使用 numberOfNodes = 3
和更多代码打印您想要的输出