Home > Software design >  Is it possible to create a csv to object converter using java generics
Is it possible to create a csv to object converter using java generics

Time:11-15

I've created a simple converter which takes String fileName and converts lines out of a .csv file into List<Cat>. The problem i'm facing now is that theres now a Dog aswell and i'm not allowed to copy and paste the method to just change return type to List<Dog>.

I've tried to use return type List<Object> to try and parse it into Cat or Dog after conversion but it won't let me. I'm looking for a generic solution to this problem if it is possible.

What i tried:

@Data
@Entity
@Table(name = "cat")
public class Cat implements Serializable {
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Id
    @Column(columnDefinition = "int(10)", nullable = false)
    int id;

    @Column(columnDefinition = "varchar(20)", nullable = false)
    String name;    
}

@Data
@Entity
@Table(name = "dog")
public class Dog implements Serializable {
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Id
    @Column(columnDefinition = "int(10)", nullable = false)
    int id;

    @Column(columnDefinition = "varchar(20)", nullable = false)
    String name;    
}

public List<Object> convertToObject(String fileName, String object) {
    List<Object> objList = new ArrayList();
    Path pathToFile = Paths.get(fileName);

    try (BufferedReader br = Files.newBufferedReader(pathToFile)) {
        int index = 1;

        // read the first line from the text file
        String line = br.readLine();

        // loop until all lines are read
        while (line != null) {

            if (index > 1) {
                switch (object) {
                    case "cat" : {
                        // use string.split to load a string array with the values from
                        // each line of
                        // the file, using a comma (,) as the delimiter
                        String[] attributes = line.split(",");

                        Cat cat = new Cat();

                        createCat(attributes, cat);

                        // adding Cat into ArrayList
                        objList.add(cat);   
                    }

                    case "dog" : {
                        // use string.split to load a string array with the values from
                        // each line of
                        // the file, using a comma (,) as the delimiter
                        String[] attributes = line.split(",");

                        Dog dog = new Dog();

                        createDog(attributes, dog);

                        // adding Dog into ArrayList
                        objList.add(dog);   
                    }
                }
            }

            // read next line before looping
            // if end of file reached, line would be null
            line = br.readLine();

            index  ;
        }

    } catch (IOException ex) {
        ex.printStackTrace(System.out);
    }

    return objList;
}

CodePudding user response:

You can use Java Generics

Create a CSV Utility class.

public class CsvUtils<T> {

    public List<T> read(final String fileName, final String object) throws IOException {
        
        // put your logic here to read the csv file (convertToObject method)
      
    }

}

Then you can simply use it in this way:

    public void doSomeStuffWithMyDogs() throws IOException {
         
         CsvUtils<Dog> csvUtils = new CsvUtils<>();
         List<Dog> myDogs = csvUtils.read("MyDogs_V1.csv", "dog");
      
         // do somthing else

    }

    public void doSomeStuffWithMyCats(final String fileName) throws IOException {
        
         CsvUtils<Cat> csvUtils = new CsvUtils<>();
         List<Cat> myCats = csvUtils.read("MyCats_V1.csv", "cat");
      
         // do somthing else

    }

My opinion:
I don't like your convertToObject implementation:
1.

//Why are you creating a new instance of Cat here?
Cat cat = new Cat();

// This method is called createCat...
// Why did you pass a instance of Cat? this will update the cat, not create a new cat.
// It is also supposed to return the cat created.
createCat(attributes, cat);

objList.add(cat);

The method createCat can return a Cat object, and the new cat can be created inside the method, so you can remove it from parameter.

then you can write:

Cat cat = createCat(attributes);
objList.add(cat);

or in a single line:

objList.add(createCat(attributes));

2.
if next month you will add Horse to project, you will need to change the implementation, adding the switch case "horse", and you also need to implement a new method createHorse(String[] attributes, String object).

Imagine if next year, your project will need to handle all animals, you will go crazy.

You need to abstract as much as possible to avoid this problem. You don't need at all to implement the algorithm on your own. There are a lot of libraries that parse CSV file into Java Object.

As example: (Apache Commons CSV, OpenCSV, SimpleFlatMapper CSV parser, jackson-dataformat-csv, uniVocity-parsers, deephaven-csv), but there are many more!

Documentation for Generics:

  • Related