Groovy load .csv files - groovy

How to read and import .csv file in groovy on grails. I have .csv file with data and
need to import in to db using user interface .

There are as always different possibilities to work with CSV files in Groovy.
As Groovy is fully interoperable with Java, you can use one of the existing CSV libararies, e.g. OpenCSV.
Depending on the complexity of the CSV file you are using, you can also use the standard file/string handling possibilities of Groovy:
def sql = Sql.newInstance("jdbc:mysql://localhost:3306/mydb",
"user", "pswd", "com.mysql.jdbc.Driver")
def people = sql.dataSet("PERSON")
new File("users.csv").splitEachLine(",") {fields ->
people.add(
first_name: fields[0],
last_name: fields[1],
email: fields[2]
)
}
EDIT:
Kelly Robinson just wrote a nice blog post about the different possibilities that are available to work with CSV files in Groovy.
EDIT #2:
As Leonard Axelsson recently released version 1.0 of his GroovyCVS library, I thought I should definitely add this to the list of options.

Using xlson's GroovyCSV:
#Grab('com.xlson.groovycsv:groovycsv:1.3')
import static com.xlson.groovycsv.CsvParser.parseCsv
for(line in parseCsv(new FileReader('countries.csv'), separator: ';')) {
println "Country=$line.COUNTRY, Capital=$line.CAPITAL"
}
The field names are taken from the header of the CSV file.
If the CSV file has no header, you can specify the field names programmatically.

With Apache Commons-CSV
#Grab('org.apache.commons:commons-csv:1.2')
import org.apache.commons.csv.CSVParser
import static org.apache.commons.csv.CSVFormat.*
import java.nio.file.Paths
Paths.get('countryInfo.txt').withReader { reader ->
CSVParser csv = new CSVParser(reader, DEFAULT.withHeader())
for (record in csv.iterator()) {
println record.dump()
}
}
Commons-CSV has nice API and I recommend that.
With GroovyCSV:
#Grab('com.xlson.groovycsv:groovycsv:0.2')
import com.xlson.groovycsv.CsvParser
def csv = '''Name,Lastname
Mark,Andersson
Pete,Hansen'''
def data = new CsvParser().parse(csv)
for(line in data) {
println "$line.Name $line.Lastname"
}
(Taken from it's samples)
Last resort: Regular expression.
Here's how I parsed a file that might contain a quoted escaped string in it's fourth column:
File detailedStatsFile = new File("stats.csv");
detailedStatsFile.eachLine { line, number ->
// Number Of Executions, Total Milliseconds, Milliseconds per execution, "Type"
def match = line =~ /([^,]*?),\s*([^,]*?),\s*([^,]*?),\s*(?:([^",]+)|(?:"((?:[^\\"]++(?:\\")?)++)"))$/; //"
if (!match.matches())
continue;
def numberOfExecs = Integer.valueOf(match.group(1));
def totalMillis = Integer.valueOf(match.group(2));
def detailedStatName = match.group(4);
if (detailedStatName == null)
detailedStatName = match.group(5).replaceAll('\\"','"');

Example using opencsv
#Grab('com.opencsv:opencsv:4.0')
import com.opencsv.CSVReader
import com.opencsv.CSVWriter
class TestCsvReader {
static main(args) {
def csv = '''"a","b","c"
"d","e","f"
'''
def Reader csvFileReader = new StringReader(csv)
def Writer csvFileWriter = new PrintWriter(System.out)
def CSVReader reader = new CSVReader(csvFileReader)
def CSVWriter writer = new CSVWriter(csvFileWriter)
reader.iterator().each { fields ->
writer.writeNext(fields)
}
reader.close()
writer.close()
}
}

Example from production code done by SAP developers in SCPi SAP Cloud Platform Integration groovy iFlow:
String[] parseCSVLine(String line) {
// Create a pattern to match breaks
Pattern p =
Pattern.compile(",(?=([^\"]*\"[^\"]*\")*(?![^\"]*\"))");
// Split input with the pattern
String[] fields = p.split(line);
for (int i = 0; i < fields.length; i++) {
// Get rid of residual double quotes
fields[i] = fields[i].replace("\"", "");
}
return fields;
}
Usage:
cols = parseCSVLine(line)
value = cols[0]
value = cols[1]

I prefer a slight tweak on the accepted answer: zip the columns and values together, as opposed to indexing each one by number. The result is slightly shorter code.
def sql = Sql.newInstance("jdbc:mysql://localhost:3306/mydb", "user", "pswd", "com.mysql.jdbc.Driver")
def people = sql.dataSet("PERSON")
def columns = ['first_name', 'last_name', 'email']
new File("users.csv").splitEachLine(",") {values ->
people.add([columns, values].transpose().collectEntries())
}

Related

Groovy script to split a file line at ',' and create a new XML file

I am very new to Groovy and trying to figure my way out.
I am trying to write a groovy to split the lines of a file on encountering ',' and then write a if condition based on the first two characters of the line. After that I wanted to create a XML file using the different data in the file. This is how far I have reached.
def Message processData(Message message) {
//Body
def body = message.getBody(java.lang.String)as String;
def varStringWriter = new StringWriter();
def varXMLBuilder = new MarkupBuilder(varStringWriter);
String newItem ;
body.eachLine{
line -> newItem = line ;
String newItem1 = newItem.substring(0,2).trim();
String newItem2 = newItem.substring(3,11).trim();
varXMLBuilder.RECORD{
node1(newItem1);
node2(newItem2);
}
}
def xml = varStringWriter.toString();
xml="<RECORDS>"+xml+"</RECORDS>" ;
message.setBody(xml);
return message;
}
In the above code I tried to use offset but, since each of my file lines are of different length it wont work.
Please help me handle this issue.
Regards,
Nisha
Splitting on a character can be done like this:
data = 'axaratgxrgc,rxregxsergcs'
def lines = data.split(/,/)
assert lines[0] == 'axaratgxrgc'
assert lines[1] == 'rxregxsergcs'
Welcome, first of all, to groovy and Stack Overflow :)
You can use tokenize() to split a string, as shown bellow.
And yeah, don't worry about ; in groovy ;)
def Message processData(Message message) {
//Body
def body = message.getBody(java.lang.String) as String;
def varStringWriter = new StringWriter()
def varXMLBuilder = new MarkupBuilder(varStringWriter)
body.eachLine { line ->
def newItems = line.tokenize(',') // input is a list of chars that will split your string, usually better than .split()
String newItem1 = newItems.first() // looks like you want just two items
String newItem2 = newItems.last() // but you can use as an array as well newItems[0] and newItems[1]
varXMLBuilder.RECORD {
node1(newItem1)
node2(newItem2)
}
}
def xml = varStringWriter.toString()
xml="<RECORDS>${xml}</RECORDS>" // you can use ${} to add a variable inside a string
message.setBody(xml)
return message
}

Read CSV file and put result in a map using Groovy (without using any external libraries)

I have a CSV file like below :
Sno,Service,Operation,ResponseTimeLimit
1,ProposalService,upsert,50
2,ScheduleService,getReservation,10
3,ScheduleService,bookAppointment,23
I would like to get this structure as the list of map as below :
[
[Sno:"1", Service:"ProposalService",Operation:"upsert",ResponseTimeLimit:"50"],
[Sno:"2",Service:"ScheduleService",Operation:"getReservation",ResponseTimeLimit:"10"],
[Sno:"3",Service:"ScheduleService",Operation:"bookAppointment",ResponseTimeLimit:"23"]
]
I got solution using CSVReader (OpenCSV). But can i do this WITHOUT USING any external references/libraries (like openCSV etc.)?
If the structure of the CSV file will always be the same, you could do it with a Groovy script:
def mapList = []
File csvFile = new File("/path/to/your/file.csv")
csvFile.eachLine { line ->
def parts = line.split(",")
def tmpMap = [:]
tmpMap.putAt("Sno", parts[0])
tmpMap.putAt("Service", parts[1])
// etc.
mapList.add(tmpMap)
}
Below is another way to iterate all rows of the csv file:
def mapList = []
def headers = []
new File("/path/to/your/file.csv").readLines().eachWithIndex { row, rowIndex ->
if (rowIndex == 0) { headers = row.split(',') }
else {
def tmpMap = [:]
def cells = row.split(',').eachWithIndex { cell, cellIndex ->
tmpMap[headers[cellIndex]] = cell
}
mapList.add(tmpMap)
}
}
mapList result:
[[Sno:1, Service:ProposalService, Operation:upsert, ResponseTimeLimit:50], [Sno:2, Service:ScheduleService, Operation:getReservation, ResponseTimeLimit:10], [Sno:3, Service:ScheduleService, Operation:bookAppointment, ResponseTimeLimit:23]]
[Finished in 1.647s]`

How to convert soap xml response to delimited

I don't know hardly anything about XML. I have successfully gotten a SOAP response (using things like SOAPUi and Boomerang) from an asmx web service. It's a large file.
Now I need to get it to regular delimited columns. Is there a simple way to do this?
My file is attached here
Not sure if it is required one time transformation or do this job quite frequently.
So, adding the answer here with some more details.
Approach #1: Using on-line
As mentioned in the comments, you can use the on-line site to convert your xml data into csv.
Even it requires to do some pre-process with the message / response that you have i.e.,
save the data into file
remove headers or unwanted data etc or make it ready to be usable in the above mentioned online site.
The disadvantages in this approaches
requires some manual work
expose data on public, but at times may be possible to share
time taking
can not use it an automated fashion
difficult to repeat
Approach #2: Using Groovy Script
So, this approach addresses the disadvantages of #1 approach.
Here is the Groovy Script which reads previous soap request step's response, and gives the data into a csv file.
In your test case, add a new groovy script test step right after the soap request step which gives you the data and copy below script content into it. i.e., (Test Case -> Step 1: Soap Request where you are getting responseStep 2: Groovy Script (with below script))
Add a test case custom property, say OUTPUT_FILE_NAME and provide the file path for csv to be saved at. Even, if you do not provide this property, it will automatically saves the csv file chargedata.csv under System temp directory.
You may find the comments in-line
/**
* this script will read the previous step response
* extract the cdata at the given xpath
* read all the records and transfroms into csv file
**/
import com.eviware.soapui.support.XmlHolder
import groovy.xml.*
/**Define the output file name in test case custom property say OUTPUT_FILE_NAME and value as absolute file path
* otherwise, it write a file chargedata.csv in system temp directory
**/
def outputFileName = context.testCase.getPropertyValue('OUTPUT_FILE_NAME') ?: System.getProperty("java.io.tmpdir")+ '/chargedata.csv'
//csv field separator - change it if needed
def delimiter = ','
/**
* Below statement will fetch the previous request step response.
*/
def response = context.testCase.testStepList[context.currentStepIndex - 1].testRequest.response.responseContent
//Create the xml holder object to get the xpath value which cdata in this case
def responseHolder = new XmlHolder(response)
def xpath = '//*:Charges_FileResponse/*:Charges_FileResult'
//Get the cdata part from above xpath which is a string
def data = responseHolder.getNodeValue(xpath)
//This again parses the xml inside of cdata
def chargeRecords = new XmlParser().parseText(data)
//This is going hold all the data from ChargeRecords
def chargeRecordsDataStructure = []
//This is to hold all the headers
def headers = [] as Set
/**
* This is to create Charge data
**/
def buildChargeDataStructure = { charge ->
def chargeDataStructure = new Expando()
charge.children().each {
def elementName = it.name()
def elementText = it.value().join()
chargeDataStructure[elementName] = elementText
//Add to field name to the list if not already added
(elementName in headers) ?: headers << elementName
}
chargeDataStructure
}
/**
* this is to create a csv row in string format
**/
def createRow = { recordDataStructure ->
def row = new StringBuffer()
headers.each {
if (row) {
row += delimiter + recordDataStructure[it] ?: ''
} else {
row += recordDataStructure[it] ?: ''
}
}
row.toString()+'\n'
}
//Build the whole data structure of Charge Records
chargeRecords.Charge.each { charge ->
chargeRecordsDataStructure << buildChargeDataStructure( charge )
}
//Build the rows
def rows = new StringBuffer()
rows << headers.join(',') +'\n'
chargeRecordsDataStructure.each { rows << createRow (it)}
//Write the rows into file
new File(outputFileName).text = rows

How read all files in the folder and replace the pattern in file using Groovy

import groovy.io.FileType
import java.io.File;
def list = []
def dir = new File("C:\\Users\\Desktop\\CodeTest")
dir.eachFileRecurse (FileType.FILES)
{
file ->list << file
}
list.each
{
println it.path
}
//Replace the pattern in file and write to file sequentially.
def replacePatternInFile(file, Closure replaceText)
{
file.write(replaceText(file.text))
}
def file = new File(file)
def patternToFind1 = ~/</
def patternToFind2 = ~/>/
def patternToReplace1 = '&lt'
def patternToReplace2 = '&gt'
//Call the method
replacePatternInFile(file){
it.replaceAll(patternToFind1,patternToReplace1)
}
replacePatternInFile(file){
it.replaceAll(patternToFind2,patternToReplace2)
}
println file.getText()
I am able to change the pattern for one file but I want to read all the files in the folder and replace the pattern in each file one by one
while executing it:
ERROR:An error occurred [Could not find matching constructor for: java.io.File(java.util.ArrayList)], see error log for details
You have many problems with your code...
1) You don't need to import:
import java.io.File;
2) When you call:
def file = new File(file)
There is no variable called file in new File(file) (did you mean files?)
3) If you did mean new File(files) then that is where your error is... You can't make a new file from a list of Strings
4) The entity for > is > NOT &gt... The same for < (it needs a semicolon at the end)
You will need to iterate your list of Strings (files.each { path -> ?) and then work on each one in turn.
Though 2) and 3) make me suspect that the above code isn't your real code, but a pretend copy from memory (or a badly redacted copy), as the above code will not give you the error you say you're getting

How to make snakeyaml and GStrings work together

When I'm trying to use snakeyaml to dump out Yaml out of Groovy interpolated strings, it ends up printing a class name instead.
For example:
#Grab(group='org.yaml', module='snakeyaml', version='1.16')
import org.yaml.snakeyaml.Yaml
Yaml yaml = new Yaml();
def a = "a"
def list = ["$a"]
def s = yaml.dump(list)
Prints:
- !!org.codehaus.groovy.runtime.GStringImpl
metaClass: !!groovy.lang.MetaClassImpl {}
I'm guessing it has something to do with the fact that GStrings get transformed to Strings when they used and I suspect snakeyaml uses some sort of introspection to determine the class of the object.
Is there a better solution than calling toString() on all GStrings?
Try to create a new Representer :
public class GroovyRepresenter extends Representer {
public GroovyRepresenter() {
this.representers.put(GString.class, new StringRepresenter());
}
}
Yaml yaml = new Yaml(new GroovyRepresenter())
...
You could add type info to your variables
Yaml yaml = new Yaml();
def a = "a"
String aStr = "$a"
def list = [aStr]
def s = yaml.dump(list)

Resources