I parse CSV file and create a domain objects using supercsv. My domain object has one enum field, e.g.:
public class TypeWithEnum {
private Type type;
public TypeWithEnum(Type type) {
this.type = type;
}
public Type getType() {
return type;
}
public void setType(Type type) {
this.type = type;
}
}
My enum looks like this:
public enum Type {
CANCEL, REFUND
}
Trying to create beans out of this CSV file:
final String[] header = new String[]{ "type" };
ICsvBeanReader inFile = new CsvBeanReader(new FileReader(
getFilePath(this.getClass(), "learning/enums.csv")), CsvPreference.STANDARD_PREFERENCE);
final CellProcessor[] processors =
new CellProcessor[]{ TODO WHAT TO PUT HERE? };
TypeWithEnum myEnum = inFile.read(
TypeWithEnum.class, header, processors);
this fails with Error while filling an object context: null offending processor: null at org.supercsv.io.CsvBeanReader.fillObject(Unknown Source) at org.supercsv.io.CsvBeanReader.read(Unknown Source)
Any hint on parsing enums? Should I write my own processor for this?
I already tried to write my own processor, something like this:
class MyCellProcessor extends CellProcessorAdaptor {
public Object execute(Object value, CSVContext context) {
Type type = Type.valueOf(value.toString());
return next.execute(type, context);
}
}
but it dies with the same exception.
The content of my enums.csv file is simple:
CANCEL
REFUND
The exception you're getting is because CsvBeanReader cannot instantiate your TypeWithEnum
class, as it doesn't have a default (no arguments) constructor. It's probably a good idea to print the stack trace so you can see the full details of what went wrong.
Super CSV relies on the fact that you should have supplied a valid Java bean, i.e. a class with a default constructor and public getters/setters for each of its fields.
So you can fix the exception by adding the following to TypeWithEnum
:
public TypeWithEnum(){
}
As for hints on parsing enums the two easiest options are:
1. Using the HashMapper processor
@Test
public void hashMapperTest() throws Exception {
// two lines of input
String input = "CANCEL\nREFUND";
// you could also put the header in the CSV file
// and use inFile.getCSVHeader(true)
final String[] header = new String[] { "type" };
// map from enum name to enum
final Map<Object, Object> typeMap = new HashMap<Object, Object>();
for( Type t : Type.values() ) {
typeMap.put(t.name(), t);
}
// HashMapper will convert from the enum name to the enum
final CellProcessor[] processors =
new CellProcessor[] { new HashMapper(typeMap) };
ICsvBeanReader inFile =
new CsvBeanReader(new StringReader(input),
CsvPreference.STANDARD_PREFERENCE);
TypeWithEnum myEnum;
while((myEnum = inFile.read(TypeWithEnum.class, header, processors)) !=null){
System.out.println(myEnum.getType());
}
}
2. Creating a custom CellProcessor
Create your processor
package org.supercsv;
import org.supercsv.cellprocessor.CellProcessorAdaptor;
import org.supercsv.cellprocessor.ift.CellProcessor;
import org.supercsv.exception.SuperCSVException;
import org.supercsv.util.CSVContext;
public class TypeProcessor extends CellProcessorAdaptor {
public TypeProcessor() {
super();
}
public TypeProcessor(CellProcessor next) {
super(next);
}
public Object execute(Object value, CSVContext context) {
if (!(value instanceof String)){
throw new SuperCSVException("input should be a String!");
}
// parse the String to a Type
Type type = Type.valueOf((String) value);
// execute the next processor in the chain
return next.execute(type, context);
}
}
Use it!
@Test
public void customProcessorTest() throws Exception {
// two lines of input
String input = "CANCEL\nREFUND";
final String[] header = new String[] { "type" };
// HashMapper will convert from the enum name to the enum
final CellProcessor[] processors =
new CellProcessor[] { new TypeProcessor() };
ICsvBeanReader inFile =
new CsvBeanReader(new StringReader(input),
CsvPreference.STANDARD_PREFERENCE);
TypeWithEnum myEnum;
while((myEnum = inFile.read(TypeWithEnum.class, header, processors)) !=null){
System.out.println(myEnum.getType());
}
}
I'm working on an upcoming release of Super CSV. I'll be sure to update the website to make it clear that you have to have a valid Java bean - and maybe a description of the available processors, for those not inclined to read Javadoc.
Here is a generic cell processor for enums
/** A cell processor to convert strings to enums. */
public class EnumCellProcessor<T extends Enum<T>> implements CellProcessor {
private Class<T> enumClass;
private boolean ignoreCase;
/**
* @param enumClass the enum class used for conversion
*/
public EnumCellProcessor(Class<T> enumClass) {
this.enumClass = enumClass;
}
/**
* @param enumClass the enum class used for conversion
* @param ignoreCase if true, the conversion is made case insensitive
*/
public EnumCellProcessor(Class<T> enumClass, boolean ignoreCase) {
this.enumClass = enumClass;
this.ignoreCase = ignoreCase;
}
@Override
public Object execute(Object value, CsvContext context) {
if (value == null)
return null;
String valueAsStr = value.toString();
for (T s : enumClass.getEnumConstants()) {
if (ignoreCase ? s.name().equalsIgnoreCase(valueAsStr) : s.name().equals(valueAsStr)) {
return s;
}
}
throw new SuperCsvCellProcessorException(valueAsStr + " cannot be converted to enum " + enumClass.getName(), context, this);
}
}
and you will use it
new EnumCellProcessor<Type>(Type.class);
I tried to reproduce your Error but everything works for me. I use SuperCSV 1.52:
private enum ENUMS_VALUES{TEST1, TEST2, TEST3};
@Test
public void testEnum3() throws IOException
{
String testInput = new String("TEST1\nTEST2\nTEST3");
ICsvBeanReader reader = new CsvBeanReader(new StringReader(testInput), CsvPreference.EXCEL_NORTH_EUROPE_PREFERENCE);
final String[] header = new String[] {"header"};
reader.read(this.getClass(), header, new CellProcessor[] {new CellProcessorAdaptor() {
@Override
public Object execute(Object pValue, CSVContext pContext)
{
return next.execute(ENUMS_VALUES.valueOf((String)pValue), pContext);
}}});
}
@Test
public void testEnum4() throws IOException
{
String testInput = new String("TEST1\nTEST2\nTEST3");
ICsvBeanReader reader = new CsvBeanReader(new StringReader(testInput), CsvPreference.EXCEL_NORTH_EUROPE_PREFERENCE);
final String[] header = new String[] {"header"};
reader.read(this.getClass(), header, new CellProcessor[] {new CellProcessorAdaptor()
{
@Override
public Object execute(Object pValue, CSVContext pContext)
{
return ENUMS_VALUES.valueOf((String)pValue);
}}});
}
public void setHeader(ENUMS_VALUES value)
{
System.out.println(value);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With