1

I'm trying to build up a generic method that parses a CSV file into an Object.

I think I'm quite close to reach the objective but I'm a bit stuck with java generics, I'm still learning it.

Now I'm stuck on my while cycle where I create the objects. I'm using jCSV to do the parsing for me. I'm following their documentation tutorial here.

I can't figure out how to set the beanClass bc = it.next(); because beanClass does not exist as a class on my project, compilation error: cannot find symbol - class beanClass How can I fix this?

I know I could simply do a List<?> beanClassList = csvFileReader.readAll(); but the problem is that on the first line of each CSV file I've the class name to where that data belongs to. I get this exception, which makes sense:

Exception in thread "AWT-EventQueue-0" java.lang.NumberFormatException: For input string: "Car"

My CSV files are something like this:

ClassName
value,value,value,value,value
value,value,value,value,value
...

Here's my code:

public String importFromCsvFile(File f) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
    {
    FileReader fr = new FileReader(f);
    BufferedReader buffReader = new BufferedReader(fr);
    String className = buffReader.readLine();

    buffReader.close();

    //Java reflection to get the Class Object.
    Class beanClass = Class.forName("model." + className);

    Object beanObject = beanClass.newInstance();

    Reader reader = new FileReader(f);

    ValueProcessorProvider provider = new ValueProcessorProvider();
    CSVEntryParser<?> entryParser = new AnnotationEntryParser<>(beanClass, provider);
    CSVReader<?> csvFileReader= new CSVReaderBuilder<>(reader).entryParser((CSVEntryParser<Object>) entryParser).build();

    Iterator<?> it = csvFileReader.iterator();

    while (it.hasNext()) {

        beanClass bc = it.next(); // here is the compilation error

    }
}

Here's a CSV file example:

Car
1,BMW,Z3,2000,20-AC-57
2,Mercedes,C4,2010,23-32-VJ
3,Alfa Romeo,A3,1992,XX-XX-XX
dazito
  • 7,740
  • 15
  • 75
  • 117
  • variable f and csvFileReader are missing in code. – Braj Mar 09 '14 at 19:31
  • I wouldn't want a CSV parser to do anything other than spit out Strings. Your issue is probably that you're trying to cast a String to a Class object. – djb Mar 09 '14 at 19:31
  • @djb the error is: `cannot find symbol - class beanClass`. @Braj code updated, please check my updated question. – dazito Mar 09 '14 at 19:42
  • Please read about [How to create a class dynamically in java](http://stackoverflow.com/questions/4794777/how-to-create-a-class-dynamically-in-java). – Braj Mar 09 '14 at 19:43
  • You have to have a class `beanClass` in your application. What is the problem if it is created initially? Don't you know the structure of CSV file in advance? – Braj Mar 09 '14 at 19:45
  • @Braj No, I don't know the structure of the CSV beforehand. I though that having this `Class beanClass = Class.forName("model." + className);` would let me somehow be able to do what I want in the while cycle, or somewhere else. – dazito Mar 09 '14 at 19:49
  • You can't load a class until and unless its present in advance in classpath. It will throw `java.lang.ClassNotFoundException` at `Class beanClass = Class.forName("model." + className);` – Braj Mar 09 '14 at 19:50
  • To solve this problem you can create a List of HashMap having some keys and values. Define keys at the first line of CSV file and populate it with CSV values reading line by line. Should I share you a code? – Braj Mar 09 '14 at 19:54
  • @Braj following the CSV example I have in my code, I do have the Car class. But as I've Car class I can have a lot more classes. So before I read the first line in the CSV I don't know to which class I'll be working on. So again, I've all the classes in my project. I just want to make my method generic and not specific for a single class. – dazito Mar 09 '14 at 19:54
  • @Braj please share if you can :) – dazito Mar 09 '14 at 19:55
  • Please let me know if any changes is required in my code. – Braj Mar 09 '14 at 20:06
  • `beanClass` is an instance of `Class` not a type! So you can't create an instance of it by writing `beanClass myObject`. – Amir Pashazadeh Mar 09 '14 at 21:12
  • @AmirPashazadeh Yes I know, my question is exactly how to do it that way or in a different way?! – dazito Mar 09 '14 at 21:47
  • actually `it.next()` should return an instance of Car. What do you want to do with it? How do you want to construct the final String result? – Max Fichtelmann Mar 09 '14 at 22:40

3 Answers3

1

As per your comments Please have a look at sample code that reads data from CSV file and store in a map as key-value pair.

List<Map<String, String>> list = new ArrayList<Map<String, String>>();

BufferedReader reader = new BufferedReader(new FileReader(new File("resources/abc.csv")));

String header = reader.readLine();
String[] keys = header.split(",");
String line = null;
while ((line = reader.readLine()) != null) {
    Map<String, String> map = new HashMap<String, String>();
    String[] values = line.split(",");
    for (int i = 0; i < keys.length; i++) {
        map.put(keys[i], values[i]);
    }
    list.add(map);
}

reader.close();

for(Map<String, String> map:list){
    for(String key:map.keySet()){
        System.out.println(key+":"+map.get(key));
    }
    System.out.println();
}

CSV:

ID,NAME,MODEL,YEAR,NUMBER
1,BMW,Z3,2000,20-AC-57
2,Mercedes,C4,2010,23-32-VJ
3,Alfa Romeo,A3,1992,XX-XX-XX
Braj
  • 46,415
  • 5
  • 60
  • 76
  • Thanks! In `header` I've my class name, how can I create this object now? It will just be known at run time, not compile time, this is my main problem. – dazito Mar 09 '14 at 20:11
  • You can't create a class dynamically as simple as it is. What class name are you talking about. It doesn't exists in classpath in advance. You have to change your design. :) – Braj Mar 09 '14 at 20:14
1

you are nearly there.

public String importFromCsvFile(File f) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
    {
    FileReader fr = new FileReader(f);
    BufferedReader buffReader = new BufferedReader(fr);
    String className = buffReader.readLine();

    buffReader.close(); // you can also not close it and use buffReader as your reader for the CSV

    //Java reflection to get the Class Object.
    Class beanClass = Class.forName("model." + className);

    Object beanObject = beanClass.newInstance(); // this is never needed

    Reader reader = new FileReader(f); // use buffReader instead of creating a new one

    ValueProcessorProvider provider = new ValueProcessorProvider();
    CSVEntryParser<?> entryParser = new AnnotationEntryParser<>(beanClass, provider);
    CSVReader<?> csvFileReader= new CSVReaderBuilder<>(reader).entryParser((CSVEntryParser<Object>) entryParser).build();

    Iterator<?> it = csvFileReader.iterator();

    while (it.hasNext()) {

        Object obj = it.next(); // obj is an instance of Car with your data
        boolean isCar = obj instanceof Car; // will be true
    }
}

Because you are using , as separator you should consider using UK_DEFAULT as Strategy for the Reader or defining your own (the default separator is ;).

You should also either continue using the BufferedReader or specify skipHeader in the Strategy - else you Car will be treated as entry which is probably not what you want.

Max Fichtelmann
  • 3,366
  • 1
  • 22
  • 27
0

With generics, types are specified at compile time (and checked by the compiler). In your case, types are only specified in the CSV file and thus unknown at compile time. So generics is not a tool you can use in this case.

What exactly would you want to accomplish with generics? What would you like the compiler to check?

What you can do is create an instance of the class (you will need a full name including package) using Class.forName(name).newInstance() and use reflection to set some properties. But at compile time you'll only know the result is an Object.

herman
  • 11,740
  • 5
  • 47
  • 58