11

I'm trying to generate a CSV file from a rather complex Java object. The object is a Session with some attributes and a list of Strings and Messages who in turn have some attributes and a list of Comments that have some attributes.

The session class is as follows;

public class Session {

    private Long id;

    private Date startDate;

    private Date endDate;

    private List<Message> messages;

    private List<String> participants;

    public TweetSession() {
    }  

    public TweetSession(Date startDate, List<Message> messages, List<String>     participants) {
        this.startDate = startDate;
        this.messages = messages;
        this.participants = participants;
    }

    public Long getId() {
        return id;
    }

    public Date getStartDate() {
        return startDate;
    }

    public void setStartDate(Date startDate) {
        this.startDate = startDate;
    }

    public Date getEndDate() {
        return endDate;
    }

    public void setEndDate(Date endDate) {
        this.endDate = endDate;
    }

    public List<Message> getMessages() {
        return messages;
    }

    public void setMessage(List<Message> messages) {
        this.message = message;
    }

    public List<String> getParticipants() {
        return participants;
    }

    public void setParticipants(List<String> participants) {
        this.participants = participants;
    }
}

The message class is as follows;

public class Message {

    private Long id;

    private Session session;

    private Date date;

    private String participant;

    private String content;

    private List<Comment> comments;

    public Message() {
    }

    public Message(String participant, Session session, Date date, String content) {
        this.participant = participant;
        this.session = session;
        this.content = content;
        this.date = date;
        this.comments = new ArrayList<>();
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getParticipant() {
        return participant;
    }

    public void setParticipant(String participant) {
        this.participant = participant;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public List<Comment> getComments() {
        return comments;
    }

    public void setComments(List<Comment> comments) {
        this.comments = comments;
    }

    public void addComment(Comment comment) {
        this.comments.add(comment);
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    public TweetSession getSession() {
        return session;
    }

    public void setSession(TweetSession session) {
        this.session = session;
    }
}

And the Comment class;

public class Comment {

    private Long id;

    private Message message;

    private String participant;

    private String message;

    private Date date;

    public Comment() {
    }

    public Comment(String participant, Message message, String content, Date date) {
        this.participant = participant;
        this.content = content;
        this.message = message;
        this.date = date;
    }

    public String getParticipant() {
        return participant;
    }

    public void setParticipant(String participant) {
        this.participant = participant;
    }

    public Message getMessage() {
        return message;
    }

    public void setMessage(Message message) {
        this.message = message;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}   

I'm wondering if it is possible to map this to a CSV file. When I convert the session object to JSON format and convert that JSON to CSV in an online generator I get the proper output so I think it must be possible. I just don't really know how. I've tried using the net.sf.supercsv library like this;

public void generateCSV(Session session, HttpServletResponse response) throws IOException {
    String csvFileName = "session.csv";
    response.setContentType("text/csv");

    String headerKey = "Content-Disposition";
    String headerValue = String.format("attachment; filename=\"%s\"",
            csvFileName);
    response.setHeader(headerKey, headerValue);

    ICsvBeanWriter csvWriter = new CsvBeanWriter(response.getWriter(),
            CsvPreference.STANDARD_PREFERENCE);

    // Generate header for the CSV
    Field fields[] = session.getClass().getDeclaredFields();
    String[] header = new String[fields.length];
    for (int i = 0; i < fields.length; i++) {
        header[i] = fields[i].getName();
    }

    csvWriter.writeHeader(header);

    // Generate CSV content from data
    csvWriter.write(session, header);

    csvWriter.close();
}

But this will of course not give the desired result.

Can anyone point me in the right direction?

Thanks in advance,

Niels

Edit:

This is a sample session in JSON format:

{
    "id": 22,
    "startDate": 1447368081000,
    "endDate": null,
    "messages": [
        {
            "id": 10,
            "date": 1447368159000,
            "participant": "1",
            "content": "This is a message",
            "comments": []
        },
        {
            "id": 11,
            "date": 1447368168000,
            "participant": "1",
            "content": "This is also a message",
            "comments": []
        },
        {
            "id": 12,
            "date": 1447368179000,
            "participant": "1",
            "content": "This is another message",
            "comments": [
                {
                    "id": 10,
                    "participant": "1",
                    "message": "This is a comment",
                    "date": 1447368227000
                },
                {
                    "id": 11,
                    "participant": "1",
                    "message": "This is also a comment",
                    "date": 1447368234000
                }
            ]
        }
    ],
    "participants": [
        "1",
        "23"
    ]
}

When I convert this to CSV I get something like this:

CSV

Indeed starting to think (a single) CSV might not be the best approach to this problem.

Niels Masdorp
  • 2,534
  • 2
  • 18
  • 31
  • 4
    Seems to me that csv wouldn't be the appropriate format for relational data. The csv format is more appropriate for representing a table of data, with each line being semantically similar to the others. If you wanted to represent the sort of relational structure you describe you would either end up duplicating a lot of data, leaving a lot of columns blank for many rows or have to create a number of separate csv files. Could you provide us with a sample of what your expected end result should look like in the csv? – jvalli Nov 18 '15 at 19:21
  • Fair enough, the client wants the data downloaded and asked for CSV. Maybe I can convince the client to accept a different format. Thanks for your input! – Niels Masdorp Nov 18 '15 at 19:23
  • We created files for the client by creating a CSV out of a list POJOs. Please see my anwer – Mahendran Ayyarsamy Kandiar Nov 18 '15 at 19:27
  • Does your POJO consist of nested lists made up by other POJO's? – Niels Masdorp Nov 18 '15 at 19:28
  • My POJO has a List attribute – Mahendran Ayyarsamy Kandiar Nov 18 '15 at 19:32
  • Apache Common Csv api detail example: http://stackoverflow.com/a/42198895/6549532 – Kevin Feb 13 '17 at 07:52
  • Use beansToCsv approch to write content from beans to csv directly follow this link http://www.javainterviewpoint.com/csvtobean-and-beantocsv-example-using-opencsv/ – DHARMENDRA SINGH Feb 21 '18 at 04:44

5 Answers5

8

The data you have has many 1:n dependencies in it and is not really fit for a single CSV file.

Approaches I've used or seen used for this:

  • One "hybrid" CSV with Session's own data, i.e. id, startDate, endDate in first columns and then two columns for messages and participants printed as a JSON

    "123", "2015-11-17", "2015-11-18", "[{id: 345, date: ...}, {id: 789, date: ...}]", "[...]"
    

    (notice you'll need to use a good CSV library that escapes the values containing , or "s)

  • Multiple CSV files - modeled like you'd model a relational database for your structure, i.e.

    • sessions.csv containing id, startDate, endDate
    • messages.csv containing id, session_id, date, ...
    • ...

    then ZIP them for a single file download

  • Ask your user for a more precise specification - work to find out what they intend to do with the data, then give them a "view" of the data in a format that will enable them to read them easily - just the way database views and reports are created to give users a task-oriented view of data.
Jiri Tousek
  • 12,211
  • 5
  • 29
  • 43
  • Thanks for your input, I've added some sample JSON to my question. I think you are right, I don't think I should use (a single) CSV for this problem. The client just wants to be able to easily view the data in Excel or something similar. I'll try to change his mind. – Niels Masdorp Nov 18 '15 at 19:48
  • 1
    Better still, try to find out what he wants to see in that data. If he for example wants to see how messages relate to sessions, you could give him a CSV with one row for each "session - message" relation, i.e. `session_id, startDate, endDate, messageId, messageDate, messageContent` - just the way you'd prepare a view for them if they wanted to see some database data. – Jiri Tousek Nov 18 '15 at 19:52
  • I'll do that, although I think I can convince him to accept JSON or maybe XML even. – Niels Masdorp Nov 18 '15 at 20:05
2

We used openCSV

com.opencsv.bean.BeanToCsv to achieve this

   public void exportIronData(String destinationFilePath,
        List<ExportIronDataFileFormatDTO> dataList) throws Exception {
    try {
        if (validationUtil.isNullOrEmpty(destinationFilePath)
                || validationUtil.isNullOrEmpty(dataList)) {
            return;
        }
        ColumnPositionMappingStrategy<ExportIronDataFileFormatDTO> strategy = new ColumnPositionMappingStrategy<ExportIronDataFileFormatDTO>();
        strategy.setType(ExportIronDataFileFormatDTO.class);
        String[] columns = IlmcrCsvFileConstants.EXPORT_IRONDATA_COLUMN_HEADERS;
        strategy.setColumnMapping(columns);
        CSVWriter writer = new CSVWriter(
                new FileWriter(destinationFilePath),
                IlmcrCsvFileConstants.exportIronDataSeperator,
                CSVWriter.NO_ESCAPE_CHARACTER, System.getProperty("line.separator"));
        BeanToCsv<ExportIronDataFileFormatDTO> exportFormat = new BeanToCsv<ExportIronDataFileFormatDTO>();
        exportFormat.write(strategy, writer, dataList);
        writer.flush();
        writer.close();
    } catch (Exception e) {
        e.printStackTrace();
        throw e;
    }
}

Let me know if you need anything else

2

There is a library called json2flat.
What it does is it takes a complex JSON document and converts it to CSV format.

So what you need to do is convert your java objects into JSON format. After it
you need to pass the generated JSON into the library and it returns a 2D
representation of the JSON, you can also get the csv from it.

This library is not that mature but still it's promising.
You should give it a try.

skap
  • 493
  • 4
  • 9
0

You should use Hybrid CSV formats. I had same problem so I created a light weight framework. You can copy source code or example from : https://github.com/abhisoni96/dev-tools

You can create your own formatter for each collection based data members. Please refer example on above link.

-3

I recommend using Apache Commons CSV instead. We've used it on several projects, including managing our mailing lists.

 Reader in = new StringReader("a,b,c");
 for (CSVRecord record : CSVFormat.DEFAULT.parse(in)) {
     for (String field : record) {
         System.out.print("\"" + field + "\", ");
     }
     System.out.println();
 }

Output: "a", "b", "c", 
johnnieb
  • 3,982
  • 4
  • 29
  • 32