javaopencsv

Write to single cell in OPENcsv Java


I am trying to get it so that I can update one value in a CSV. The data structure is {Name, score}. Given a name, I want to increment the score by 1. I have the POJO and everything set up to grab the needed info but my issue is when I try to write to the file it will overwrite everything or nothing. I've tested my read to make sure it is getting the right name correctly and it seems to do that. So I'm trying to figure out how to fix the write function.

public static void updateCounter(String name) {
    int score = 0;

    ArrayList<String> namesArray = new ArrayList<>();
    CSVParser parser = new CSVParserBuilder()
                .withSeparator(',')
                .withIgnoreQuotations(true)
                .build();

    try (Writer writer = new FileWriter("output.txt",true)) {
        ColumnPositionMappingStrategy<Person> strategy = new ColumnPositionMappingStrategy<Person>();
        StatefulBeanToCsv<Person> sbc = new StatefulBeanToCsvBuilder<Person>(writer)
                    .withSeparator(CSVWriter.DEFAULT_SEPARATOR)
                    .withMappingStrategy(strategy)
                    .build();
    } catch (IOException e) {
        throw new RuntimeException(e);
    }

    List<Person> beans;
    try {
        beans = new CsvToBeanBuilder<Person>(new FileReader("data.csv"))
                    .withType(Person.class)
                    .build()
                    .parse();
        for (Person p : beans) {
            if (p.getName().equals(name)) {
                score = score = p.getscore() + 1;
            }
        }
        CSVReader reader;
        {
            try {
                String[] line;
                reader = new CSVReader(new FileReader("data.csv"));
                {
                    try {
                        while ((line = reader.readNext()) != null) {
                            for (String cell : line) {
                                if (cell.equals(name)) {
                                    BufferedWriter writer;
                                    {
                                        try {
                                            writer = new BufferedWriter(new FileWriter("data.csv"));

                                            writer.write(score);
                                            writer.close();
                                        } catch (IOException IOerr) {
                                            throw new RuntimeException(IOerr);
                                        }
                                    }
                                }
                            }
                        }
                    } catch (CsvException ex) {
                        throw new RuntimeException(ex);
                    }
                    reader.close();
                }
            } catch (IOException IOerr) {
                throw new RuntimeException(IOerr);
            }
        }
    } catch (FileNotFoundException ex) {
        throw new RuntimeException(ex);
    }
}

Person Class (POJO):

    import com.opencsv.bean.CsvBindByPosition;

public class Person {
    @CsvBindByPosition(position = 0)
    private String Name;
    @CsvBindByPosition(position = 1)
    private int calledTimes;

    public String getName(){
        return Name;
    }

    public void setName(String Name){
        this.Name = Name;
    }

    public int getCalledTimes(){
        return calledTimes;
    }

    public void setCalledTimes(int calledTimes){
        this.calledTimes = calledTimes;
    }
}

Sample data: enter image description here


Solution

  • I believe the following is what you require.
    (Notes after the code.)

    import com.opencsv.bean.CsvBindByName;
    import com.opencsv.bean.CsvToBeanBuilder;
    
    import java.io.BufferedWriter;
    import java.io.FileReader;
    import java.io.IOException;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.nio.file.StandardOpenOption;
    import java.util.List;
    
    public class Person {
    
        @CsvBindByName
        private String  name;
    
        @CsvBindByName
        private int score;
    
        public Person() {
            this(null, 0);
        }
    
        public Person(String name, int score) {
            this.name = name;
            this.score = score;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getScore() {
            return score;
        }
    
        public void setScore(int score) {
            this.score = score;
        }
    
        public String toString() {
            return String.format("%s,%d", name, score);
        }
    
        public static void main(String[] args) {
            try {
                List<Person> beans = new CsvToBeanBuilder<Person>(new FileReader("data.csv")).withType(Person.class)
                                                                                             .build()
                                                                                             .parse();
                for (Person p : beans) {
                    System.out.println(p);
                    if ("George".equals(p.getName())) {
                        p.setScore(p.getScore() + 1);
                    }
                }
                Path path = Paths.get("data.csv");
                try (BufferedWriter bw = Files.newBufferedWriter(path,
                                                                 StandardOpenOption.TRUNCATE_EXISTING,
                                                                 StandardOpenOption.WRITE)) {
                    bw.write("name,score");
                    bw.newLine();
                    for (Person p : beans) {
                        String line = String.format("%s,%d%n", p.getName(), p.getScore());
                        bw.write(line);
                    }
                }
            }
            catch (IOException x) {
                x.printStackTrace();
            }
        }
    }
    

    For CsvToBeanBuilder to work, Person needs to be a JavaBeans class which means it requires a no-args constructor. However that will give you a List where every Person object in that List will have null name and 0 (zero) score.

    In order to overcome this and instantiate class Person with data from file data.csv, you can use the CsvBindByName annotation. Refer to the documentation.

    After creating the List, the above code updates it. I simply used a hard-coded value, i.e. George, in order to demonstrate.

    Finally, after updating the List, I overwrite file data.csv with the contents of the updated List. I use NIO.2 for that.

    Note that the above code may not work if file data.csv is so large that it cannot be completely loaded into the computer's memory.

    Here is the sample data.csv file I used:

    name,score
    George,11
    Diego,10
    Franz,5
    Johan,8
    

    As stated in the documentation, the "header" line is required in order for the CsvBindByName annotation to work.

    The above was developed with Java 21 on Windows 10 using Eclipse 2024-09.

    Here is the module-info.java that I required.

    module opencsvm {
        opens opencsvp;
        requires com.opencsv;
        requires org.apache.commons.collections4;
        requires org.apache.commons.lang3;
        requires commons.beanutils;
        requires java.sql;
        requires org.apache.commons.logging;
    }
    

    The Apache Commons modules are required by CsvToBeanBuilder.