3

I am using opencsv to create a CsvToBean like this:

CsvToBean<AccountBean> csvToBean = new CsvToBeanBuilder<AccountBean>(new InputStreamReader(inputStream))
                .withFieldAsNull(CSVReaderNullFieldIndicator.NEITHER)
                .withType(AccountBean.class)
                .build();

And this is my AccountBean:

public class AccountBean extends BeanBase<Account>
{

    @CsvBindByName(column = "Id", required = true)
    public String salesforceId;

    @CsvBindByName(column = "OwnerId", required = true)
    public String ownerId;

    @CsvBindByName(column = "Name", required = true)
    public String name;

    // billing address
    @CsvBindByName(column = "BillingStreet")
    String billingStreet;
    @CsvBindByName(column = "BillingCity")
    String billingCity;
    @CsvBindByName(column = "BillingState")
    String billingState;
    @CsvBindByName(column = "BillingPostalCode")
    String billingPostcode;
    @CsvBindByName(column = "BillingCountry")
    String billingCountry;

}

The issue is with the address fields - if there is a blank field, they are ALWAYS null, regardless of which CSVReaderNullFieldIndicator value I pass to .withFieldAsNull().

My csv file has double quotes to denote an empty field, so using CSVReaderNullFieldIndicator.NEITHER (which is default anyway) should produce an empty String.

This is causing issues as I'm saving nulls to my datastore and then it's causing NullPointerExceptions later.

An I doing something wrong?

slugmandrew
  • 1,776
  • 2
  • 25
  • 45

2 Answers2

5

I was trying your approach and I had the same behavior. Since this library is opensource I was digging to find why it happens.

  • Inside CsvToBean Class you have a CSVReader that is responsible for access the data to be read.
  • Inside CSVReader you have a CSVParser which is responsible for take a single string and parse it into its elements based on the delimiter, quote and escape characters.
  • The CSVParser contains a member of CSVReaderNullFieldIndicator (enum) that is used to tell CSVParser what to consider null.

When you call build() in your code CsvToBean, CSVReader and CSVParser are instantiated based on the info that you passed to the CsvToBeanBuilder.

When you call parse() CSVReader will go through your CSV file and for each line it will call CSVParser. Based on your separator, the parser will return a String array of values. At this point the CSVParser, based on NullFieldIndicator, will consider to leave the string as empty or put it as null. At the end, if you have NullFieldIndicator property as NEITHER and the line considered is, for example, "one";"", the string array returned by the parser will be [one,""] or [one, null] if CSVReaderNullFieldIndicator is BOTH or EMPTY_QUOTES.

After this phase, the parsed line will be mapped to AccountBean fields. To decide either the field is null StringUtils.isNotBlank() is used.

Conclusion: No matter what you pass to withFieldAsNull(), because "" or null is considered to be false by StringUtils.isNotBlank(), therefore the field will be null.

You can ask the developer if this behavior was the expected one. Maybe he has a reason for it or it's just a bug.

3

This is going to require some thought as the issue is in BeanFieldPrimitiveTypes class which will only set a value if there is a value (so empty fields will result in a null). This is because this is is converting to all types and most do not handle empty strings (Integer). So this class needs to be modified to check the type of the field and if it is a certain set of types (String for now) then allow an empty value to be converted.

I posted the above in the bug you opened up in sourceforge and we will try and get a fix in either 4.1 or 4.2 of openCSV.

Scott Conway
  • 975
  • 7
  • 13