Home > java > Easily generate CSV in Java (e.g. from Servlet)

Easily generate CSV in Java (e.g. from Servlet)

To generate CSV from Java consider this simple interface:

package net.doepner;

import java.io.IOException;

/**
 * Generates CSV (comma separated values) for rows of Java objects
 */
public interface ICsvWriter {

    /**
     * Adds a row of objects to the CSV document
     *
     * @param values The objects in the row (count must match the number of the
     *               headers)
     */
    void row(Object... values);

    /**
     * Writes CSV based on the the String representations of the objects
     *
     * @param appendable The writer to append to
     * @throws IOException If underlying IO fails
     */
    void appendTo(Appendable appendable) throws IOException;
}

I implemented the interface with this CsvWriter class:

package net.doepner;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.regex.Pattern;

/**
 * Convenient generation of CSV
 */
public class CsvWriter implements ICsvWriter {

    private static final CharSequence CSV_ROW_END = "\r\n";
    private static final char LINE_BREAK_WITHIN_CELL = '\n';

    private static final Pattern QUOTES = Pattern.compile("\"");
    private static final String ESCAPED_QUOTE = "\"\"";

    private final Object[] headers;
    private final Collection<Object[]> rows = new LinkedList<Object[]>();

    /**
     * @param headers The objects representing the column headers
     */
    public CsvWriter(Object... headers) {
        this.headers = Arrays.copyOf(headers, headers.length);
        if (cols() == 0) {
            throw new IllegalArgumentException("No columns");
        }
    }

    @Override
    public final void row(Object... values) {
        if (values.length != cols()) {
            throw new IllegalArgumentException("Specify " + cols() + "values ");
        }
        rows.add(Arrays.copyOf(values, values.length));
    }

    @Override
    public final void appendTo(Appendable appendable) throws IOException {
        appendRow(appendable, headers);

        for (Object[] row : rows) {
            appendRow(appendable, row);
        }
    }

    private static void appendRow(Appendable appendable, Object[] row)
            throws IOException {
        boolean first = true;
        for (Object value : row) {
            if (first) {
                first = false;
            } else {
                appendable.append(",");
            }
            appendable.append('"');
            appendable.append(toCsvString(value));
            appendable.append('"');
        }
        appendable.append(CSV_ROW_END);
    }

    private static CharSequence toCsvString(Object object) {
        if (object instanceof Iterable) {
            final StringBuilder sb = new StringBuilder();
            boolean first = true;
            for (Object o : (Iterable<?>) object) {
                if (first) {
                    first = false;
                } else {
                    sb.append(LINE_BREAK_WITHIN_CELL);
                }
                sb.append(toCsvString(o));
            }
            return sb.toString();
        } else {
            if (object == null) {
                return "";
            } else {
                final String s = object.toString();
                return QUOTES.matcher(s).replaceAll(ESCAPED_QUOTE);
            }
        }
    }

    private int cols() {
        return headers.length;
    }
}

Example for how it can be used in a Servlet, here with full support for Unicode characters using UTF-8, in a way that even Excel understands:

    private static final char BYTE_ORDER_MARK = (char) 0xfeff;
 
    private static void generateCsv(ServletResponse resp, 
                                    Iterable<IMessage> messages)
            throws ServletException {

        resp.setContentType("text/csv");
        resp.setCharacterEncoding("UTF-8");

        final ICsvWriter csv = new CsvWriter(
                "Date", "Sender", "Recipients", "Subject");

        for (IMessage msg : messages) {
            csv.row(msg.getDateTime(), msg.getSender(), 
                    msg.getRecipients, msg.getSubject());
        }

        final PrintWriter writer = getResponseWriter(resp);
        try {
            // The BOM is required so that Excel will recognize UTF-8
            // characters properly, i.e. all non-ASCII letters, etc.
            writer.print(BYTE_ORDER_MARK);
            writer.flush();

            csv.appendTo(writer);
            writer.flush();

        } catch (IOException e) {
            throw new ServletException(e);
        }
    }

    private static PrintWriter getResponseWriter(ServletResponse resp)
            throws ServletException {
        try {
            return resp.getWriter();
        } catch (IOException e) {
            throw new ServletException(e);
        }
    }
Advertisements
Categories: java
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: