While writing Beans to CSV file by using OpenCSV 4.6, all the headers are changing to uppercase. Eventhough bean has @CsvBindByName annotation it is changing to uppercase.
Java Bean:
public class ProjectInfo implements Serializable {
@CsvBindByName(column = "ProjectName",required = true)
private String projectName;
@CsvBindByName(column = "ProjectCode",required = true)
private String projectCode;
@CsvBindByName(column = "Visibility",required = true)
private String visibility;
//setters and getters
}
Main method
public static void main(String[] args) throws IOException {
Collection<Serializable> projectInfos = getProjectsInfo();
try(BufferedWriter writer = new BufferedWriter(new FileWriter("test.csv"))){
StatefulBeanToCsvBuilder builder = new StatefulBeanToCsvBuilder(writer);
StatefulBeanToCsv beanWriter = builder
.withSeparator(';')
.build();
try {
beanWriter.write(projectInfos.iterator());
writer.flush();
} catch (CsvDataTypeMismatchException | CsvRequiredFieldEmptyException e) {
throw new RuntimeException("Failed to download admin file");
}
}
}
Expected Result:
"ProjectCode";"ProjectName";"Visibility"
"ANY";"Country DU";"1"
"STD";"Standard";"1"
"TST";"Test";"1"
"CMM";"CMMTest";"1"
Acutal Result:
"PROJECTCODE";"PROJECTNAME";"VISIBILITY"
"ANY";"Country DU";"1"
"STD";"Standard";"1"
"TST";"Test";"1"
"CMM";"CMMTest";"1"
I don't have option to use ColumnMappingStrategy because I have to build this method as a generic solution. can anyone suggest me how to write the headers as it is?
Writing a CSV file is as simple as reading. Create an instance of CSVWriter by passing FileWriter object as parameter and start writing data to CSV file using methods of CSVWriter Class. After writing data we need to close CSVWriter connection by calling close() method of CSVWriter class.
The file is created and the headers are inserted, but all the headers in same cell. csvFile. createNewFile(); CSVWriter csvWrite = new CSVWriter(new FileWriter(csvFile)); String heading = "eid,name,vid,emp_id,balance_amt,handover_to,entry_date \n"; csvWrite. writeNext(new String[]{heading});
This class writes beans out in CSV format to a Writer , keeping state information and making an intelligent guess at the mapping strategy to be applied. This class implements multi-threading on writing more than one bean, so there should be no need to use it across threads in an application.
It happens, because the code in HeaderColumnNameMappingStrategy uses toUpperCase()
for storing and retrieving the field names.
You could use the HeaderColumnNameTranslateMappingStrategy instead and create the mapping by reflection.
public class AnnotationStrategy extends HeaderColumnNameTranslateMappingStrategy
{
public AnnotationStrategy(Class<?> clazz)
{
Map<String,String> map=new HashMap<>();
//To prevent the column sorting
List<String> originalFieldOrder=new ArrayList<>();
for(Field field:clazz.getDeclaredFields())
{
CsvBindByName annotation = field.getAnnotation(CsvBindByName.class);
if(annotation!=null)
{
map.put(annotation.column(),annotation.column());
originalFieldOrder.add(annotation.column());
}
}
setType(clazz);
setColumnMapping(map);
//Order the columns as they were created
setColumnOrderOnWrite((a,b) -> Integer.compare(originalFieldOrder.indexOf(a), originalFieldOrder.indexOf(b)));
}
@Override
public String[] generateHeader(Object bean) throws CsvRequiredFieldEmptyException
{
String[] result=super.generateHeader(bean);
for(int i=0;i<result.length;i++)
{
result[i]=getColumnName(i);
}
return result;
}
}
And, assuming that there is only one class of items (and always at least one item), the creation of beanWriter
has to be expanded:
StatefulBeanToCsv beanWriter = builder.withSeparator(';')
.withMappingStrategy(new AnnotationStrategy(projectInfos.iterator().next().getClass()))
.build();
Actually, HeaderColumnNameMappingStrategy uses toUpperCase() for storing and retrieving the field names. In order to use custom field name you have to annotate you field with @CsvBindByName
@CsvBindByName(column = "Partner Code" )
private String partnerCode;
By default it will be capitalized to PARTNER CODE because of the above reason. so, in order to take control over it we have to write a class implementing HeaderColumnNameTranslateMappingStrategy. With csv 5.0 and java8 i have implemented like this
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import com.opencsv.bean.CsvBindByName;
import com.opencsv.bean.HeaderColumnNameTranslateMappingStrategy;
import com.opencsv.exceptions.CsvRequiredFieldEmptyException;
public class AnnotationStrategy<T> extends HeaderColumnNameTranslateMappingStrategy<T> {
Map<String, String> columnMap = new HashMap<>();
public AnnotationStrategy(Class<? extends T> clazz) {
for (Field field : clazz.getDeclaredFields()) {
CsvBindByName annotation = field.getAnnotation(CsvBindByName.class);
if (annotation != null) {
columnMap.put(field.getName().toUpperCase(), annotation.column());
}
}
setType(clazz);
}
@Override
public String getColumnName(int col) {
String name = headerIndex.getByPosition(col);
return name;
}
public String getColumnName1(int col) {
String name = headerIndex.getByPosition(col);
if(name != null) {
name = columnMap.get(name);
}
return name;
}
@Override
public String[] generateHeader(T bean) throws CsvRequiredFieldEmptyException {
String[] result = super.generateHeader(bean);
for (int i = 0; i < result.length; i++) {
result[i] = getColumnName1(i);
}
return result;
}
}
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