OpenCsvReader.iterator() parses content line
Brought to you by:
aruckerjones,
sconway
I want to read a csv file line by line using a CsvToBean reader. All members of the bean-to-read are marked as mandatory.
public class TestBean
{
@CsvBindByName(column = "COLUMN_A", required = true)
String columnA;
@CsvBindByName(column = "COLUMN_B", required = true)
String columnB;
}
The input file consists of the header line and a content line where one column is empty.
When the CsvToBean.iterator() is created it throws a runtime exception with cause CsvRequiredFieldEmptyException.
@Test
public void testOpenCsvIterator()
{
Reader reader = new StringReader("COLUMN_A,COLUMN_B\n,b");
CsvToBean<TestBean> csvReader = new CsvToBeanBuilder<TestBean>(reader) //
.withType(TestBean.class) //
.withSeparator(',') //
.build();
final Iterator<TestBean>[] iterator = new Iterator[] { null };
assertDoesNotThrow(() -> {
iterator[0] = csvReader.iterator();
});
}
The iterator() call should not read a content line. Only the first iterator.next() call should parse the content line and throw an exeption.
Sorry for the delay in the response but work happens :)
My rejection is based around your last statement.
Iterator is an interface - how it is implemented is wholly up to whomever implements the class. Which allows for the implementor to add desired features like caching/buffering.
Besides what's the best way to implement the hasNext method? Go ahead and read the next record.
Of course you are right: the implementation of the iterator() interface is up to the implementor.
But the current implementation makes it very hard to differentiate between an error in the header line and an error in the first content line.
file1.csv
COLUMN_A, COLUMN_B
,b
throws
java.lang.RuntimeException: com.opencsv.exceptions.CsvRequiredFieldEmptyException: Field 'columnA' is mandatory but no value was provided.
file2.csv
COLUMN_A, COLUMN_C
a, b
throws
java.lang.RuntimeException: Error capturing CSV header!
Two RuntimeException which have to be parsed using the exception message. If the iterator() call would only check the header line the error handling would be much easier.
Sorry Michael for the delay again - work, family, and all the life stuff again as always :)
I see your frustration but I do not believe it is hard to differentiate - just confusing because it happened before what you think a read has happened. But the error stated that a mandatory field is missing information.
That said what surporised me reading the code is that internally it very specifically reads the first record after reading the header. Unfortunately it was done in a private inner class with little documentation but it was done on purpose - you can see that it was the intent be reading the code. However I cannot tell if the intent was to prime the hasNext which I alluded to in my first response or to check if the record matched the class it was being mapped to or if it was a simple sanity check or all three. But it was done on purpose so changing it would potentially break that purpose.
Now if you are not strapped for memory I would recommend using streams.
Modifying your code to use streams (note bean name changed because there are multiple test beans in the code <bg></bg>
Yields a slightly more detailed error with the actual line that caused the error
Last edit: Scott Conway 2025-10-22