4

I have multiple csv files containing about 200 to 300 columns and I need to create pojos out of them with a mapping of one to one (column too java class field). Never mind the fact that is not recommended. If you know a tool or how to do this automatically please pitch in.

so you have a csv file, containing thousands of rows with hundreds of columns, the first row contains the header of the columns. SO what I need, based on the first row (header of column) to create a java class that contains those headers as class fields. Never mind the actual data. I just need a java class with those fields

There was a question regarding somewhat this post but this was asked about 3 years ago, so I guess is obsolete.

8
  • You can use Javassist to generate classes at runtime Commented May 28, 2015 at 11:50
  • Can you provide an example for the one to one mapping/of a csv file? Commented May 28, 2015 at 12:06
  • one csv column should generate one class field, so 255 csv column should get me a class with 255 fields :) Commented May 28, 2015 at 12:10
  • I asked for the mapping and assumed you mean the possibility to assign row object references to row objects... Commented May 28, 2015 at 12:14
  • 2
    I have to ask - what would be the use of such generated classes? The only possible use I can think of is to obfuscate & bake in the CSV data... Commented May 28, 2015 at 12:40

3 Answers 3

8

You can use Javassist to generate classes at runtime:

Code:

public static void main(String[] args) throws Exception {
    String[] fieldNames = null;
    Class<?> rowObjectClass = null;
    try(BufferedReader stream = new BufferedReader(new InputStreamReader(Program.class.getResourceAsStream("file.csv")))) {
        while(true) {
            String line = stream.readLine();
            if(line == null) {
                break;
            }
            if(line.isEmpty() || line.startsWith("#")) {
                continue;
            }
            if(rowObjectClass == null) {
                fieldNames = line.split(",");
                rowObjectClass = buildCSVClass(fieldNames);
            } else {
                String[] values = line.split(",");
                Object rowObject = rowObjectClass.newInstance();
                for (int i = 0; i < fieldNames.length; i++) {
                    Field f = rowObjectClass.getDeclaredField(fieldNames[i]);
                    f.setAccessible(true);
                    f.set(rowObject, values[i]);

                }
                System.out.println(reflectToString(rowObject));
            }
        }
    }
}

private static int counter = 0;
public static Class<?> buildCSVClass(String[] fieldNames) throws CannotCompileException, NotFoundException {
    ClassPool pool = ClassPool.getDefault();
    CtClass result = pool.makeClass("CSV_CLASS$" + (counter++));
    ClassFile classFile = result.getClassFile();
    ConstPool constPool = classFile.getConstPool();
    classFile.setSuperclass(Object.class.getName());
    for (String fieldName : fieldNames) {
        CtField field = new CtField(ClassPool.getDefault().get(String.class.getName()), fieldName, result);
        result.addField(field);
    }
    classFile.setVersionToJava5();
    return result.toClass();
}

public static String reflectToString(Object value) throws IllegalAccessException {
    StringBuilder result = new StringBuilder(value.getClass().getName());
    result.append("@").append(System.identityHashCode(value)).append(" {");
    for (Field f : value.getClass().getDeclaredFields()) {
        f.setAccessible(true);
        result.append("\n\t").append(f.getName()).append(" = ").append(f.get(value)).append(", ");
    }
    result.delete(result.length()-2, result.length());
    return result.append("\n}").toString();
}


Resources:

file.csv (classpath):

############
foo,bar
############
hello,world
cafe,babe


Output:

CSV_CLASS$0@1324706137 {
    foo = hello, 
    bar = world
}
CSV_CLASS$0@1373076110 {
    foo = cafe, 
    bar = babe
}
Sign up to request clarification or add additional context in comments.

4 Comments

I was doing the same... I can stop now x)
I'm interested in your solution :)
seems like my answer, I'll give it a try right away
Be aware that the resource file file.csv has to be in the same directory and also add a ´` in front of it. So call the method on "/file.csv". I cannot correct that in the code, because an edit has to change at least six characters.
1

As per my undersatnding, you are trying to read a csv file with large range of columns, being treated it as a table for database

My solution is as follows

  • Use csvjdbc to query the data columns

This can be done by using the file as data source and csvjdbc driver, and using the Metadata, you can retrieve all the columns

  • Create a runtime POJO Class using tool Provider

This can be done and a refernce is at here

Comments

1

This version doesn't use any fancy class-generating code; instead, it outputs Java source-files (or more accurately, a single class file, with a static public inner subclass for each non-header row in the CSV).

For the following input (borrowed from Binkan):

############
foo,bar
############
he"l\nl"o,world
cafe,babe

The output would be:

public class Out {
    public static class Row1 {
        public String foo = "he\"l\\nl\"o";
        public String bar = "world";
    }
    public static class Row2 {
        public String foo = "cafe";
        public String bar = "babe";
    }
}

And here is the code; partially adapted from Binkan's as regards line-by-line reading:

public class T {

    public static void classesFromRows(String fileName, PrintWriter out, String classNamePrefix) throws Exception{
        try(BufferedReader stream = new BufferedReader(new FileReader(fileName))) {
            String line = null;
            String[] fields = null;
            int rowNum = 0;
            while ((line = stream.readLine()) != null) {
                if (line.isEmpty() || line.startsWith("#")) {
                    // do nothing
                } else if (fields == null) {
                    fields = line.split(",");
                } else {
                    rowNum ++;
                    String[] values = line.split(",");
                    out.println("\tpublic static class " + classNamePrefix + rowNum + " {");
                    for (int i=0; i<fields.length; i++) {
                        out.println("\t\tpublic String " + fields[i] + " = \"" 
                            + StringEscapeUtils.escapeJava(values[i]) + "\";");
                    }
                    out.println("\t}");
                }           
            }
        }
    }

    // args[0] = input csv; args[1] = output file
    public static void main(String[] args) throws Exception {       
        File outputFile = new File(args[1]);
        String outputClass = outputFile.getName().replace(".java", "");
        PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(outputFile)));
        // missing: add a package here, if you want one
        out.println("public class " + outputClass + " {");
        classesFromRows(args[0], out, "Row");
        out.println("}");
        out.close();
    }
}

I have chosen to consider all data as strings (on the bright side, I am escaping them correctly using StringEscapeUtils); with some more code, you could specify other types.

2 Comments

I don't think it is intended to put all rows in separate classes rather than one with described fields in the first row
That is possible; and my code would be easy to fix to handle it. Still, I don't think either the above code or a revised version would be of much use to anyone, beyond proving how easy it is to write a primitive source-code generator.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.