Is there a Java library to generate .java Bean files based on CSV files? [closed]

StackOverflow https://stackoverflow.com/questions/23676681

  •  23-07-2023
  •  | 
  •  

Question

For example if I had a CSV file like this:

name, age, height
joe, 23, 180.5

I need a Blah.java bean file to be automatically generated like this:

public class Blah{

  String name;
  int age;
  float height;

  // getters and setters...
}

Is there a library that would accomplish that? I of course searched, but came up empty handed.

Était-ce utile?

La solution

I'm not aware of an existing library that is able to do this (and I did not do a websearch, because most likely, you already did this before asking the question).

(EDIT: It seems that the library used in the answer that was linked by bchetty is at least close to what you have been looking for)

For the simplest case, this is done with a few lines of code - although, strictly speaking, it is not possible at all: From a CSV file, you don't obtain any information about the types of the columns. (In the example below, I solved this by manually specifying the types of the columns).

Additionally, as in many cases, it's easy to write a "simple" solution, but after the first shot, the requirements are extended, and it turns out that using a sophisticated solution right from the beginning would have been beneficial. So if you are going to extend this later for

  • Configurable formatting (indentation, brackets...)
  • Indexed properties
  • Non-primitive value types (E.g. a Person that contains a Address object as property)
  • Bound propertes (i.e. a PropertyChangeSupport)
  • ... or anything else that goes beyond writing some getters+setters

then you should consider using an infrastructure that allows you to describe the class as such. I'm currently thinking about something like Eclipse JDT, but of course, for the current question this would be an overkill.

EDIT 2: Based on the comment, extended the snippet with some code that ""infers"" the type of the columns. Note that this is a very simple and pragmatic approach, it does not do sophisticated error checking, and there are situations where the outcome may not be the desired one. E.g. when one column contains the string 100 in the first row, then the column type will be inferred as int, although in the second row, the same column could contain the string 123.456, which should be considered as a double - not to talk about missing or special values, e.g. like - meaning something like "No data available". When something can not be undoubtedly inferred to be int or double based on the first row, it is considered as a String.

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


public class BeanGenerator
{
    public static void main(String[] args) throws IOException
    {
        //testManualSetup();
        testAutomaticSetup();
    }

    private static void testManualSetup()
    {
        List<BeanProperty> beanProperties = 
            new ArrayList<BeanProperty>();
        beanProperties.add(new BeanProperty("name", String.class));
        beanProperties.add(new BeanProperty("age", Integer.TYPE));
        beanProperties.add(new BeanProperty("height", Double.TYPE));

        String result = generateBeanFile("Person", "", beanProperties);
        System.out.println(result);
    }

    private static void testAutomaticSetup() throws IOException
    {
        List<BeanProperty> beanProperties = inferBeanProperties("test.csv"); 

        System.out.println("Inferred properties:");
        for (BeanProperty beanProperty : beanProperties)
        {
            System.out.println(beanProperty);
        }

        String result = generateBeanFile("Person", "", beanProperties);
        System.out.println(result);
    }


    static List<BeanProperty> inferBeanProperties(String fileName) throws IOException
    {
        FileInputStream fileInputStream = null;
        try
        {
            fileInputStream = new FileInputStream(fileName);
            return inferBeanProperties(fileInputStream);
        }
        finally
        {
            if (fileInputStream != null)
            {
                fileInputStream.close();
            }
        }
    }


    private static List<BeanProperty> inferBeanProperties(
        InputStream inputStream) throws IOException
    {
        final String delimiter = ",";
        BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
        List<String> names = null;
        List<Class<?>> types = null;
        while (true)
        {
            String line = br.readLine();
            if (line == null)
            {
                return Collections.emptyList();
            }
            if (line.trim().length()==0)
            {
                continue;
            }
            if (names == null)
            {
                names = tokenize(line, delimiter);
            }
            else
            {
                types = inferTypes(line, delimiter);
                break;
            }
        }
        List<BeanProperty> beanProperties = new ArrayList<BeanProperty>();
        for (int i=0; i<names.size(); i++)
        {
            String name = names.get(i);
            Class<?> type = types.get(i);
            BeanProperty beanProperty = new BeanProperty(name, type);
            beanProperties.add(beanProperty);
        }
        return beanProperties;
    }

    private static List<String> tokenize(String line, String delimiter)
    {
        List<String> list = new ArrayList<String>();
        String tokens[] = line.split(delimiter);
        for (String token : tokens)
        {
            list.add(token.trim());
        }
        return list;
    }
    private static List<Class<?>> inferTypes(String line, String delimiter)
    {
        List<String> tokens = tokenize(line, delimiter);
        List<Class<?>> types = new ArrayList<Class<?>>();
        for (String token : tokens)
        {
            types.add(inferType(token));
        }
        return types;
    }


    private static Class<?> inferType(String token)
    {
        try
        {
            Integer.parseInt(token);
            return Integer.TYPE;
        }
        catch (NumberFormatException e)
        {
            // Ignored
        }
        try
        {
            Double.parseDouble(token);
            return Double.TYPE;
        }
        catch (NumberFormatException e)
        {
            // Ignored
        }
        return String.class;
    }


    static class BeanProperty
    {
        private final String name;
        private final Class<?> type;

        BeanProperty(String name, Class<?> type)
        {
            this.name = name;
            this.type = type;
        }

        String getName()
        {
            return name;
        }

        Class<?> getType()
        {
            return type;
        }

        @Override
        public String toString()
        {
            return name+": "+type;
        }
    }

    static String generateBeanFile(String beanName,
        String header, 
        List<BeanProperty> beanProperties)
    {
        StringBuilder sb = new StringBuilder();

        sb.append(header);

        sb.append("public class "+beanName+"\n");
        sb.append("{"+"\n");
        for (BeanProperty beanProperty : beanProperties)
        {
            sb.append("    private ");
            sb.append(beanProperty.getType().getSimpleName());
            sb.append(" ");
            sb.append(decapitalize(beanProperty.getName()));
            sb.append(";"+"\n");
        }
        sb.append("\n");
        sb.append("    public "+beanName+"()"+"\n");
        sb.append("    {"+"\n");
        sb.append("        // Default constructor"+"\n");
        sb.append("    }"+"\n");
        for (BeanProperty beanProperty : beanProperties)
        {
            sb.append("\n");
            sb.append(createSetterString(beanProperty));
            sb.append("\n");
            sb.append(createGetterString(beanProperty));
        }
        sb.append("}"+"\n");
        return sb.toString();
    }

    private static String createSetterString(BeanProperty beanProperty)
    {
        StringBuilder sb = new StringBuilder();

        sb.append("    public void set");
        sb.append(capitalize(beanProperty.getName()));
        sb.append("(");
        sb.append(beanProperty.getType().getSimpleName());
        sb.append(" ");
        sb.append(decapitalize(beanProperty.getName()));
        sb.append(")"+"\n");

        sb.append("    {"+"\n");

        sb.append("        this.");
        sb.append(decapitalize(beanProperty.getName()));
        sb.append(" = ");
        sb.append(decapitalize(beanProperty.getName()));
        sb.append(";"+"\n");

        sb.append("    }"+"\n");

        return sb.toString();
    }

    private static String createGetterString(BeanProperty beanProperty)
    {
        StringBuilder sb = new StringBuilder();

        sb.append("    public ");
        sb.append(beanProperty.getType().getSimpleName());
        sb.append(" get");
        sb.append(capitalize(beanProperty.getName()));
        sb.append("()"+"\n");

        sb.append("    {"+"\n");

        sb.append("        return ");
        sb.append(decapitalize(beanProperty.getName()));
        sb.append(";"+"\n");

        sb.append("    }"+"\n");

        return sb.toString();
    }

    private static String decapitalize(String s)
    {
        char c = Character.toLowerCase(s.charAt(0));
        return c + s.substring(1);
    }

    private static String capitalize(String s)
    {
        char c = Character.toUpperCase(s.charAt(0));
        return c + s.substring(1);
    }

}

Autres conseils

You can use Apache-Commons-CSV to parse the CSV file and then use CGLIB to generate Java class files at runtime.

Although I didn't use it so far, SuperCSV [that you already had in your tags :)] has 2 custom bean readers. CsvBeanReader and CsvDozerBeanReader . See below a sample from their website.

/**
 * An example of reading using CsvBeanReader.
 */
private static void readWithCsvBeanReader() throws Exception {

        ICsvBeanReader beanReader = null;
        try {
                beanReader = new CsvBeanReader(new FileReader(CSV_FILENAME), CsvPreference.STANDARD_PREFERENCE);

                // the header elements are used to map the values to the bean (names must match)
                final String[] header = beanReader.getHeader(true);
                final CellProcessor[] processors = getProcessors();

                CustomerBean customer;
                while( (customer = beanReader.read(CustomerBean.class, header, processors)) != null ) {
                        System.out.println(String.format("lineNo=%s, rowNo=%s, customer=%s", beanReader.getLineNumber(),
                                beanReader.getRowNumber(), customer));
                }

        }
        finally {
                if( beanReader != null ) {
                        beanReader.close();
                }
        }
}

UPDATE

Please be aware that you can not create the java class itself since you need to already have to know what POJOs it's reading from the file. If you need to actually create class files, you mght want to consider using the CsvListReader to read the lines, and then manually generate the classes with getters and setters. However, you may need to include additional metadata such as "field types".

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top