panagulis72
panagulis72

Reputation: 2169

Java csv parser/writer

I'm trying to get a CSV from some data retrieved by Oracle. I have just to write the csv, using the result of the query as column of csv. This is my code:

    // get data
    final List<myDto> dataCsv = myDao.getdata();

    StringWriter writer = new StringWriter();
    CSVWriter csvWriter = new CSVWriter(writer,';');

    List<String[]> result  = toStringArray(dataCsv);
    csvWriter.writeAll(result);
    csvWriter.close();

    return Response.ok(result).header("Content-Disposition", "attachment; filename=" + fileName).build();`

Obviously it can't find toStringArray(). But have I to build it? Do I really need it? How do I have to edit the edit to get it working?

Upvotes: 1

Views: 1979

Answers (2)

Basil Bourque
Basil Bourque

Reputation: 338326

Apache Commons CSV

The Apache Commons CSV library can help with the chore of reading/writing CSV files. It handles several variants of the CSV format, including the one used by Oracle.

CSVFormat.ORACLE

Employee.java

Let's make a class for Employee.

package work.basil.example;

import java.util.Objects;

public class Employee {
    public Integer id;
    public String name, role;
    public Integer salary;

    public Employee ( Integer id , String name , String role , Integer salary ) {
        Objects.requireNonNull( id ); // etc.
        this.id = id;
        this.name = name;
        this.role = role;
        this.salary = salary;
    }

    @Override
    public String toString ( ) {
        return "Employee{ " +
                "id=" + id +
                " | name='" + name + '\'' +
                " | role='" + role + '\'' +
                " | salary=" + salary +
                " }";
    }
}

Example app

Make another class to mimic retrieving your DTOs. Then we write to a CSV file.

Obviously it can't find toStringArray(). But have I to build it? Do I really need it? How do I have to edit the edit to get it working?

To answer your specific Question, there is no toStringArray method to create field values for the CSV from your DTO object‘s member variables.

Binding

This idea of mapping input or output data with member variables of a Java object is generally known as binding.

There are sophisticated binding libraries for Java to bind your objects with XML and with JSON, JAXB and JSON-B respectively. Objects can be automatically written out to XML or JSON text, as well as “re-hydrated” back to objects when read from such XML or JSON text.

But for CSV with a simpler library such as Apache Commons CSV, we read and write each field of data individually for each object. You pass each DTO object member variable, and Commons CSV will write those values out to the CSV text with any needed encapsulating quote marks, commas, and escaping.

You can see this in the code below, in this line:

printer.printRecord( e.id , e.name , e.role , e.salary );

EmployeeIo.java

Here is the entire EmployeeIo.java file where Io means input-output.

package work.basil.example;

import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

public class EmployeeIo {
    public static void main ( String[] args ) {
        EmployeeIo app = new EmployeeIo();
        app.doIt();
    }

    private void doIt ( ) {
        // Mimic a collection of DTO objects coming from the database.
        List < Employee > employees = new ArrayList <>( 3 );
        employees.add( new Employee( 101 , "Alice" , "Boss" , 11_000 ) );
        employees.add( new Employee( 102 , "Bob" , "Worker" , 12_000 ) );
        employees.add( new Employee( 103 , "Carol" , "Worker" , 13_000 ) );

        Path path = Paths.get( "/Users/basilbourque/Employees.csv" );
        this.write( employees , path );
    }


    public void write ( final List < Employee > employees , final Path path ) {
        try ( final CSVPrinter printer = CSVFormat.ORACLE.withHeader( "Id" , "Name" , "Role" , "Salary" ).print( path , StandardCharsets.UTF_8 ) ; ) {
            for ( Employee e : employees ) {
                printer.printRecord( e.id , e.name , e.role , e.salary );
            }
        } catch ( IOException e ) {
            e.printStackTrace();
        }
    }

}

When run, we produce a file.

Screenshot showing the data of a CSV file, three rows of data plus a header row, with columns for Id, Name, Role, and Salary.

Upvotes: 1

ManoDestra
ManoDestra

Reputation: 6473

If you just follow the example from the link that you've given, you'll see what they're doing...

private static List<String[]> toStringArray(List<Employee> emps) {
        List<String[]> records = new ArrayList<String[]>();
        //add header record
        records.add(new String[]{"ID","Name","Role","Salary"});
        Iterator<Employee> it = emps.iterator();
        while(it.hasNext()){
            Employee emp = it.next();
            records.add(new String[]{emp.getId(),emp.getName(),emp.getRole(),emp.getSalary()});
        }
        return records;
    }

Essentially, you need to build a List of String[]. Each String[] represents a single line of data for the CSV, with each element of the array being a value. So, yes, you need to build a List from your data model and pass it to the CSVWriter's writeAll() method.

The first String[] in the list is the column headers for the CSV. The subsequent String arrays are the data itself.

Upvotes: 2

Related Questions