ozlerhakan/poiji

Default value for list of strings

milospopovic opened this issue · 11 comments

Hi,

when I want to set a property of type List<String> and null is not preferred over default value, when a cell value is an empty string, I expect default value to be set as empty list. Instead, I get an exception that "" cannot be converted into list of strings: java.lang.IllegalArgumentException: Can not set java.util.List field ... to java.lang.String

Thanks a lot.

Thank you for contributing to Poiji! Feel free to create a PR If you want to contribute directly :)

Hi @milospopovic ,

Can you provide your case with an example?

Hi, sure.

I have list of strings in my model:

public class Employee {
    @ExcelCellName("emails")
    List<String> emails;
}

In Excel, there is an empty cell:

emails

Now I want to convert this excel into Employee:

PoijiOptions options = PoijiOptionsBuilder.settings()
                       .preferNullOverDefault(false) 
                       .build();
Poiji.fromExcel(new File("employees.xls"), Employee.class, options)

The expected behavior is that I get an empty list of strings. Instead, I get an IllegalArgumentException.

Thanks.

Hi @milospopovic ,

preferNullOverDefault does not work with List. You can do :

@ExcelCellName("emails")
List<String> emails = new ArrayList<>();
stale commented

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

I still have an issue with that in the latest version (4.0.0).

@ExcelCellName(value = "Components", mandatoryHeader = true)
private List<String> components = new ArrayList<>();

Exception:

java.lang.IllegalArgumentException: Can not set java.util.List field my.package.MyEntity.components to java.lang.String
	at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
	at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
	at java.base/jdk.internal.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:81)
	at java.base/java.lang.reflect.Field.set(Field.java:799)
	at com.poiji.bind.mapping.HSSFUnmarshaller.setFieldData(HSSFUnmarshaller.java:291)
	at com.poiji.bind.mapping.HSSFUnmarshaller.constructTypeValue(HSSFUnmarshaller.java:281)
	at com.poiji.bind.mapping.HSSFUnmarshaller.tailSetFieldValue(HSSFUnmarshaller.java:235)
	at com.poiji.bind.mapping.HSSFUnmarshaller.mapColumns(HSSFUnmarshaller.java:226)
	at com.poiji.bind.mapping.HSSFUnmarshaller.tailSetFieldValue(HSSFUnmarshaller.java:198)
	at com.poiji.bind.mapping.HSSFUnmarshaller.setFieldValuesFromRowIntoInstance(HSSFUnmarshaller.java:300)
	at com.poiji.bind.mapping.HSSFUnmarshaller.deserializeRowToInstance(HSSFUnmarshaller.java:171)
	at com.poiji.bind.mapping.HSSFUnmarshaller.processRowsToObjects(HSSFUnmarshaller.java:96)
	at com.poiji.bind.mapping.SheetUnmarshaller.unmarshal(SheetUnmarshaller.java:38)
	at com.poiji.bind.Poiji.fromExcel(Poiji.java:427)

How I read the sheet:

Poiji.fromExcel(sheet, MyEntity.class, MAIN_SHEET_OPTIONS, entry -> {
    // [...]
});

private static final PoijiOptions MAIN_SHEET_OPTIONS = PoijiOptions.PoijiOptionsBuilder.settings()
    .headerStart(3)
    .build();

The reason for this is because cellValue.isEmpty() is checked prior to checking if the target type is a List. See here:

} else if (value.isEmpty()) {
o = options.preferNullOverDefault() ? null : value;
} else if (fieldType == List.class) {
o = castListValue(value, sheetName, row, col, field, options);
} else {

Due to that, o is a String where the type of the field is a List. And when trying to set the value, an exception is thrown.

Hi @DManstrator , let's take it back to the issue tracker. We can fix it with a minor version.

Thank you very much for the quick response, @ozlerhakan!

Another issue I noticed: When castListValue would be called, o would be a list as expected. However, it holds an empty string if the original value was empty.

image

For non-empty values it works as expected.

So what is it supposed to be?

Sorry if I wasn't clear. If a cell is empty I would expect an empty List, not a List containing an empty String as an element.

If the cell contains one or more elements (e.g. foobar, hello, world), it already works accordingly.

The problem with the empty value is that "".split(options.getListDelimiter()) will create an array with an empty String as an element. And with Arrays.asList(valueList), this is converted to a List with an empty String as an element.

Since the problem is also present with other types such as ints, doubles, etc., I guess it would be better to remove the empty String right from the beginning. Something like that:

String[] valueList = value.split(options.getListDelimiter());
// Storing the Stream to not iterate all elements twice.
Stream<String> valueStream = Stream.of(valueList)
    .filter(Predicate.not(String::isEmpty));

I can confirm - at least for List<String> - that it is working as expected with v4.1.0. Thank you very much!