如何根据变量值识别 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 和更多代码打印您想要的输出