30

I'm looking for a library that allows me to map a .csv contents to an object.

Something like:

public class Person {

    private String name;
    private int age;

    @CsvField("name")
    public String getName() {
        return this.name;
    }

    @CsvField("age")
    public int getAge() {
        return this.age;
    }
}

and then say something like:

final Person filledWithDataFromCsv = csvApi.load(csvFilepath, Person.class);

from the given CSV:

#name, age
tom, 11
jim, 32

Does anyone know of such an API, or one that does something similar. I don't want it to use annotations as a must, I just want to be able to load the file with one line of code and a predefined class.

Stephen Kennedy
  • 20,585
  • 22
  • 95
  • 108
Simeon
  • 7,582
  • 15
  • 64
  • 101
  • 4
    It should be pretty easy to write one, however something which does it already would be better. I wouldn't use annotations either, I would just use the field names. ;) – Peter Lawrey Jun 20 '11 at 12:50
  • I do not think that exists, however I could be wrong – RMT Jun 20 '11 at 12:51
  • @Peter why do you prefer field names over annotations ? I always tend to turn away from the reflection based approaches. – Simeon Jun 20 '11 at 12:56
  • 3
    @Simeon, You would have to use reflections to get the annotations. Do the annotations add value, otherwise they just make your code more verbose? – Peter Lawrey Jun 20 '11 at 12:57
  • @Peter well they decouple your field names from the csv, so you can rename freely. Otherwise if you decide that a new name better fits a given field, but you have already given your CSV format to a client you can no longer change your object member names (not without savage hacking anyway ...) :) – Simeon Jun 20 '11 at 13:02
  • @simeon With annotations won't you need to tag your setter fields as well as your getter fields? – Atreys Jun 20 '11 at 13:02
  • @Simeon, If you make that change you are better off defining the mapping externally, other wise you can only have one mapping for all versions of a file type ever. – Peter Lawrey Jun 20 '11 at 13:04
  • @Peter hmm, agreed. I think I'm convinced :) – Simeon Jun 20 '11 at 13:05
  • @Atreys no, you can either tag setters, getters or fields depending on preference. Hibernate/JPA uses the getters. – Simeon Jun 20 '11 at 13:06

5 Answers5

26

JSefa allow you to annotate Java classes that can be used in a serialization and de-serialization process. The tutorial demonstrates how this works with the CsvIOFactory class.

(From the tutorial) Annotating a bean is as simple as specifying the locations of the items in the list of values, and if necessary, you'll need to specify the conversion format:

@CsvDataType()
public class Person {
    @CsvField(pos = 1)
    String name;

    @CsvField(pos = 2, format = "dd.MM.yyyy")
    Date   birthDate;
}
Vineet Reynolds
  • 76,006
  • 17
  • 150
  • 174
12

I prefer opencsv, it is ultra simple and very clean.

http://opencsv.sourceforge.net/

For example reading:

CSVReader reader = new CSVReader(new FileReader("yourfile.csv"));
String [] nextLine;
while ((nextLine = reader.readNext()) != null) {
    // nextLine[] is an array of values from the line
    System.out.println(nextLine[0] + nextLine[1] + "etc...");
}
Thomas Jungblut
  • 20,854
  • 6
  • 68
  • 91
11

You can't go wrong with uniVocity-parsers. It supports all sorts of powerful operations and it is much faster than any other CSV parser for Java.

Here's a class with some examples:

class TestBean {

    // if the value parsed in the quantity column is "?" or "-", it will be replaced by null.
    @NullString(nulls = { "?", "-" })
    // if a value resolves to null, it will be converted to the String "0".
    @Parsed(defaultNullRead = "0")
    private Integer quantity;   // The attribute type defines which conversion will be executed when processing the value.

    @Trim
    @LowerCase
    // the value for the comments attribute is in the column at index 4 (0 is the first column, so this means fifth column in the file)
    @Parsed(index = 4)
    private String comments;

    // you can also explicitly give the name of a column in the file.
    @Parsed(field = "amount")
    private BigDecimal amount;

    @Trim
    @LowerCase
    // values "no", "n" and "null" will be converted to false; values "yes" and "y" will be converted to true
    @BooleanString(falseStrings = { "no", "n", "null" }, trueStrings = { "yes", "y" })
    @Parsed
    private Boolean pending;
}

Here's how to get a list of TestBean

BeanListProcessor<TestBean> rowProcessor = new BeanListProcessor<TestBean>(TestBean.class);

CsvParserSettings parserSettings = new CsvParserSettings();
parserSettings.setRowProcessor(rowProcessor);
parserSettings.setHeaderExtractionEnabled(true);

CsvParser parser = new CsvParser(parserSettings);
parser.parse(getReader("/examples/bean_test.csv"));

List<TestBean> beans = rowProcessor.getBeans();

Disclosure: I am the author of this library. It's open-source and free (Apache V2.0 license).

josliber
  • 43,891
  • 12
  • 98
  • 133
Jeronimo Backes
  • 6,141
  • 2
  • 25
  • 29
  • `getReader` is not defined? – Govind Singh Aug 24 '15 at 11:27
  • Just use new FileReader(new File("path/to/file.csv")); – Jeronimo Backes Aug 24 '15 at 15:15
  • Can JeronimoBackes or @josliber answer this one please: http://stackoverflow.com/questions/36647873/need-to-map-multiple-columnames-to-a-single-field-in-univocity – Koustav Ray Apr 15 '16 at 16:00
  • I see an Apache License version of the project at https://github.com/uniVocity/univocity-parsers/# but see an entirely different site that talks about it being free for non-commerical use http://www.univocity.com/products/license Can you clarify the differences between the Apache version and the version on the univocity.com site? – Sanjiv Jivan May 16 '16 at 16:16
  • Univocity-parsers is free. The univocity framework is commercial. – Jeronimo Backes May 16 '16 at 19:23
  • @JeronimoBackes i was trying to use it but readOrder was null for me at BeanConversionProcessor - and rowProcessor.getBeans() came back as null. what can i do to fix it ? – 2Big2BeSmall Jun 14 '17 at 09:39
  • 1
    This answer should be accepted as the best answer, especially the `map field by header name` feature. I can't find the same feature in other CSV parser. – min Mar 28 '18 at 07:53
  • 1
    This seriously needs to be voted up. I spent all day trying to use JSEFA pulling my hair out trying to get it to work, and then I found this and had it running like clockwork within 10 minutes! Thank you for your great work! – Daryn May 16 '18 at 06:46
4

There are pretty good JDBC implementations for CSV. So you can make use of such a driver, configure a datasource to use it and then use JPA (or whatever) for object-relational mapping on top of that data source.

Costi Ciudatu
  • 37,042
  • 7
  • 56
  • 92
3

SimpleFlatMapper can do that easily see Getting Started csv using the headers of the csv or by manually specifying which columns map to what property.

CsvParser
    .mapTo(MyObject.class)
    .forEach(file, System.out::println);
user3996996
  • 342
  • 3
  • 5