Reading CSV Files
- Adding Apache commons csv library as dependencies
- Putting your csv file in the project
- Read the CSV file in your code
Adding apache commons text library as dependencies
Put the following in your build.gradle dependencies{} section. The current version of commons cvs is 1.6. You might want to use a newer version upon its release.
compile group: 'org.apache.commons', name: 'commons-csv', version: '1.6'
Putting your csv file in the project
For Grails 3 project, one of the good place to store your file is the /grails-app/conf directory. You can even create your own directory within it. We are going to use MTR Lines (except Airport Express & Light Rail) Fares mtr_lines_fares.csv as an example, and it can be downloaded at here.
The data look like the follow. Play attention to the header.
| SRC_STATION_NAME | SRC_STATION_ID | DEST_STATION_NAME | DEST_STATION_ID | OCT_ADT_FARE | OCT_STD_FARE | SINGLE_ADT_FARE | OCT_CON_CHILD_FARE | OCT_CON_ELDERLY_FARE | OCT_CON_PWD_FARE | SINGLE_CON_CHILD_FARE |
|---|---|---|---|---|---|---|---|---|---|---|
| Central | 1 | Central | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| Central | 1 | Admiralty | 2 | 4.6 | 3 | 5 | 3 | 2 | 2 | 3 |
| … | ||||||||||
Read the CSV file in your code
We can read the csv in a Grails controller or service. We are going to do this in a controller.
class PriceController {
def grailsResourceLocator
def index() {
String strings = grailsResourceLocator.findResourceForURI('classpath:/mtr_lines_fares.csv').inputStream.getText(StandardCharsets.UTF_8.name())
Iterable<CSVRecord> records = CSVFormat.RFC4180.withFirstRecordAsHeader().parse(new StringReader(strings))
StringBuilder sb = new StringBuilder()
for (CSVRecord cvsRecord : records) {
try {
sb.append(cvsRecord.get("SRC_STATION_NAME")).append(", ")
sb.append(cvsRecord.get("SRC_STATION_ID")).append(", ")
sb.append(cvsRecord.get("DEST_STATION_NAME")).append(", ")
sb.append(cvsRecord.get("DEST_STATION_ID")).append(", ")
sb.append(cvsRecord.get("OCT_ADT_FARE")).append(", ")
sb.append(cvsRecord.get("OCT_STD_FARE")).append(", ")
sb.append(cvsRecord.get("SINGLE_ADT_FARE")).append(", ")
sb.append(cvsRecord.get("OCT_CON_CHILD_FARE")).append(", ")
sb.append(cvsRecord.get("OCT_CON_ELDERLY_FARE")).append(", ")
sb.append(cvsRecord.get("OCT_CON_PWD_FARE")).append(", ")
sb.append(cvsRecord.get("SINGLE_CON_CHILD_FARE")).append(", ")
sb.append(cvsRecord.get("SINGLE_CON_ELDERLY_FARE"))
sb.append("<br>")
} catch (Exception ignored) {
log.error("Unable to convert ${cvsRecord.toString()}")
}
}
render(sb.toString())
}
}
First we load our csv file with grailsResourceLocator.
String strings = grailsResourceLocator.findResourceForURI('classpath:/mtr_lines_fares.csv').inputStream.getText(StandardCharsets.UTF_8.name())
Than parse it with withFirstRecordAsHeader. It will return an iterable CSVRecord, which is basically a map with headers as keys.
Iterable<CSVRecord> records = CSVFormat.RFC4180.withFirstRecordAsHeader().parse(new StringReader(strings))
With the iterable records, we can use a loop to loop though them to extract the data we want. Here we use the csv header as the key to obtains data in each column.
for (CSVRecord cvsRecord : records) {
try {
sb.append(cvsRecord.get("SRC_STATION_NAME")).append(", ")
.
.
.
} catch (Exception ignored) {
log.error("Unable to convert ${cvsRecord.toString()}")
}
CSV without header
You can parse the string with
Iterable<CSVRecord> records = CSVFormat.RFC4180.parse(new StringReader(strings))
And loop though the records with
for (CSVRecord cvsRecord : records) {
try {
sb.append(cvsRecord.get(0).append(", ")
sb.append(cvsRecord.get(1).append(", ")
sb.append(cvsRecord.get(2).append(", ")
.
.
.
} catch (Exception ignored) {
log.error("Unable to convert ${cvsRecord.toString()}")
}
