Как я могу идентифицировать строку в csv-файле на основе значения переменной и начать перебор строк файла с этой строки и далее в Groovy?

#csv #groovy #jmeter

Вопрос:

Я использую Jmeter для отправки нескольких HTTPS — запросов в какой-либо API. Мне нужно каждый раз составлять 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>
 

Can someone please tell me how this can be done?

UPDATE:
Thanks to @injecteer, I ended up with this code which, in my implementation, does not work as intended :

 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 amp;amp; 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...
 

Комментарии:

1. Я изо всех сил пытался понять вашу проблему и потерпел неудачу. Обеспечьте текущие и желаемые выходные данные

2. @injecteer Спасибо за отзыв. Я обновил вопрос. Надеюсь, теперь в этом больше смысла.

3. Я понимаю. Я обновил строки «плохое поведение» в вашем вопросе и пометил их // @injecteer

4. @injecteer Большое спасибо! теперь это работает! Мне пришлось прокомментировать инструкции журнала, потому что они пытались напечатать ArrayList и log.info() применимо только для строковых типов аргументов.

Ответ №1:

Я бы изложил код естественным заводным способом.

ОБНОВЛЕНИЕ: Я добавил еще несколько строк 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 amp;amp; 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 помощью и более кода выведите желаемый вывод вашего

Комментарии:

1. Это здорово, но как я могу начать с верхней части CSV-файла, чтобы создать внутри 3 или более объектов, если я дойду до конца файла? Мне нужно определить, сколько объектов <объект><объект></объект> мне нужно внутри тегов </объект><узлы> <узлы></узлы>. Если я определяю переменную с номером 3 или 99, а длина файла невелика, мне нужно, чтобы итерация файла продолжалась от верхней части файла до нижней снова и снова и продолжала составлять объекты <объект><объект></объект> до тех пор, пока не будет достигнуто целевое число.

2. смотрите обновление..

3. хм, это может быть связано с ограничениями Дженкинса… пробовать new File('').text.readLines()

4. ты забыл " в конце: log.info "$data -> ${data.getClass()}"

5. опубликуйте весь код в своем первоначальном вопросе

Ответ №2:

Если вы хотите получить количество строк, в которых Dominique присутствует текст, это можно сделать с помощью функции findIndexOf (), например:

 def dominiquePosition = lines.findIndexOf { line -> line.contains('Dominique') }
 

затем вы можете начать создавать свой XML-файл с позиции, полученной в приведенной выше строке:

 dominiquePosition.upto(numberOfNodes, { lineNo ->
 

Итерация может быть получена с помощью vars.getIteration() функции, где vars обозначает экземпляр класса JMeterVariables. Смотрите 8 Лучших классов Java JMeter, которые вы должны использовать в статье Groovy, для получения более подробной информации об этом и других сокращениях API JMeter, доступных для тестовых элементов JSR223

Комментарии:

1. Спасибо, Дмитрий! Это работает, но в конечном итоге я получаю исключение IndexOutOfBoundsException при достижении конца CSV-файла. Как я могу смягчить это? Кроме того, вы упомянули vars.getIteration() —> Почему вы думаете, что мне это нужно ?