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<>();
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:
poiji/src/main/java/com/poiji/config/DefaultCasting.java
Lines 293 to 297 in 241d99d
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.
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!