I have the following CSV file:
iDocType,iDocId,iName,iDate
P,555551555,Braiden,2022-12-31
I,100000001,Dominique,2024-12-10
P,100000002,Joyce,2025-11-15
Using jmeter's JSR223 preprocessor element, I need to compose an XML parent node containing multiple (based on parametrization) child-nodes and each node must contain properties with values of each of these CSV rows.
I think that I need some way to loop over this CSV file and extract values from each row until all of my target objects are composed.
Maybe the approach should be having a method called createMasterXml
with 2 arguments like findTargetIdInCsv
and targetNumberOfXmlNodes
and a for loop inside which parses the csv file and composes child node inside it with the groovy.xml.MarkupBuilder. But I don't know how to approach the problem.
Target logic:
For example, given the csv file described above:
I get a variable docId populated with the value 100000001 which is found on the 2nd row of data in the csv file (ignoring the header);
I define a variable numberOfNodes = 3;
Then I expect an object created by this mapping:
child node 1 - ValuesFromCsvRow2
child node 2 - ValuesFromCsvRow3
child node 3 - ValuesFromCsvRow1
Update: JSR223 PreProcessor code: (Note, with this current approach I am not able to compose the sub-nodes objects based on my intended logic described above, because the current approach does not handle parsing the CSV file and extracting values - I am missing the knowledge to do that)
//input from csv file
docType = vars.get('iDocType').toString()
docId = vars.get('iDocId').toString()
name = vars.get('iName').toString()
date = vars.get('iExpiryDate').toString()
def numberOfNodes = 3
def writer = new StringWriter()
def xml = new groovy.xml.MarkupBuilder(writer)
xml.nodes() {
createNode(xml, numberOfNodes, 'ID0000')
}
def createNode(builder, repeat, pReqID) {
for (int i = 0 ; i < repeat ; i++) {
builder.object(a:'false', b:'false', pReqID:pReqID +(i+1).toString()) {
builder.al(ad:'2021-09-20', alc:'bla', bla:'2021-09-20T11:00:00.000Z', sn:'AB8912')
builder.doc( docType: docType, docId: docId, name: name , date:date )
}
}
}
def nodeAsText = writer.toString()
//log.info(nodeAsText)
vars.put('passengers3XmlObj', nodeAsText)
The values from the code line with builder.doc
is the one which I need to change on each node creation, based on the values from each line in the source csv file.
Currently, in this situation, my master object looks like this, because in each jmeter iteration I know only how to get the values from one row from the csv file, per sampler (using CSV Data Set test plan element):
<objects>
<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' date='2024-12-10' name='Dominique' />
</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' date='2024-12-10' name='Dominique' />
</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='100000001' date='2024-12-10' name='Dominique' />
</object>
</objects>
But, I need it to look like this, keeping in mid the target logic:
<objects>
<object a='false' b='false' c='ID00001'>
<al ad='2021-09-20' alc='bla' dt='2021-09-20T11:00:00.000Z' sn='AB8912' />
<doc docType='I' docId='100000001' date='2024-12-10' name='Dominique' />
</object>
<object a='false' b='false' c='ID00002'>
<al ad='2021-09-20' alc='bla' dt='2021-09-20T11:00:00.000Z' sn='AB8912' />
<doc docType='P' docId='100000002' date='2025-11-15' name='Joyce' />
</object>
<object a='false' b='false' c='ID00003'>
<al ad='2021-09-20' alc='bla' dt='2021-09-20T11:00:00.000Z' sn='AB8912' />
<doc docType='P' docId='555551555' date='2022-12-31' name='Braiden' />
</object>
</objects>
Can someone please help me achieve this ?
CSV Data Set Config by default reads next line on each iteration of each virtual user. The behaviour is controllable up to certain extend by the Sharing Mode setting but none of the sharing modes is suitable for reading the whole content of the CSV file at once.
If you want to parse all the entries from the CSV file in a single shot - do the reading/parsing in Groovy itself
Something like:
def writer = new StringWriter()
def xml = new groovy.xml.MarkupBuilder(writer)
def pReqID = 'ID0000'
def lines = new File('test.csv').readLines()
xml.nodes() {
1.upto(lines.size() - 1, { 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()