Statefulbeantocsv With Column Headers

StatefulBeanToCsv with Column headers

ColumnPositionMappingStrategy#generateHeader returns empty array

/**
* This method returns an empty array.
* The column position mapping strategy assumes that there is no header, and
* thus it also does not write one, accordingly.
* @return An empty array
*/
@Override
public String[] generateHeader() {
return new String[0];
}

If you remove MappingStrategy from BeanToCsv builder

// replace 
StatefulBeanToCsv<Product> beanWriter = builder.withMappingStrategy(mappingStrategy).build();
// with
StatefulBeanToCsv<Product> beanWriter = builder.build();

It will write Product's class members as CSV header

If your Product class members names are

"productCode", "MFD", "EXD"

This should be the right solution

Else, add @CsvBindByName annotation

import com.opencsv.bean.CsvBindByName;
import com.opencsv.bean.StatefulBeanToCsv;
import com.opencsv.bean.StatefulBeanToCsvBuilder;

import java.io.FileWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;

public class CsvTest {

public static void main(String[] args) throws Exception {
Writer writer = new FileWriter(fileName);

StatefulBeanToCsvBuilder<Product> builder = new StatefulBeanToCsvBuilder<>(writer);
StatefulBeanToCsv<Product> beanWriter = builder.build();

List<Product> products = new ArrayList<>();
products.add(new Product("1", "11", "111"));
products.add(new Product("2", "22", "222"));
products.add(new Product("3", "33", "333"));
beanWriter.write(products);
writer.close();
}

public static class Product {
@CsvBindByName(column = "productCode")
String id;
@CsvBindByName(column = "MFD")
String member2;
@CsvBindByName(column = "EXD")
String member3;

Product(String id, String member2, String member3) {
this.id = id;
this.member2 = member2;
this.member3 = member3;
}

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getMember2() {
return member2;
}

public void setMember2(String member2) {
this.member2 = member2;
}

public String getMember3() {
return member3;
}

public void setMember3(String member3) {
this.member3 = member3;
}
}

}

Output:

"EXD","MFD","PRODUCTCODE"

"111","11","1"

"222","22","2"

"333","33","3"

Pay attention; class, getters & setters needs to be public due to the use of Reflection by OpenCSV library

Why opencsv capitalizing csv headers while writing to file

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();

Appending to CSV file without headers

Good one. Appending was something we did not think much of when we did writing in opencsv because it is potentially risky (something goes wrong you could corrupt what was a good file) so write was favored instead.

That said open up a bug or feature request in sourceforge and if there is enough interest we will try and get it in the 4.3 release (4.2 is booked solid).

THAT said if you want to get around this create your own MappingStrategy class that extends the HeaderColumnNameMappingStrategy and all you need there is a override on the generateHeader method to return an empty String array. You can look at the code in ColumnPositionMappingStrategy to see what I am talking about. This will prevent the header from being written. In this case you will need to use this only in the else part.

Hope that helps.

:)

When writing a file produced by opencsv in java how do I create a file with no heading?

Put the annotation

 @CsvBindByPosition(position = 0)

On the bean being serialised.

OpenCsv writes wrong column names with BeanToCsv + HeaderColumnNameTranslateMappingStrategy

Looking at the source code of BeanToCsv, the processHeader(...) method does nothing with the supplied headers. Your only option is to create a custom strategy ( to avoid CSVReader ) and a custom BeanToCsv as below




public class CustomBean {
...
public static void main(String[] args) {
...
HeaderColumnNameTranslateMappingStrategy strategy = new CustomStrategy<CustomBean>();
strategy.setType(CustomBean.class);
strategy.setColumnMapping(mapping);
...

BeanToCsv bean = new CustomBeanToCsv<CustomBean>();
...
}

static class CustomStrategy<T> extends HeaderColumnNameTranslateMappingStrategy {

@Override
public void setColumnMapping(Map columnMapping) {
super.setColumnMapping(columnMapping);
header = new String[columnMapping.size()];
int i = 0;
for (Map.Entry entry : columnMapping.entrySet()) {
header[i] = entry.getKey().toUpperCase();
i++;
}
}

public String[] getHeader() {
return header;
}
}

static class CustomBeanToCsv<T> extends BeanToCsv {
@Override
protected String[] processHeader(MappingStrategy mapper) throws IntrospectionException {
if (mapper instanceof CustomStrategy) {
return ((CustomStrategy) mapper).getHeader();
} else {
return super.processHeader(mapper);
}
}
}
}


OpenCSV all of the data storing in single line version (5.1) and data loss

When you try to write more than one object the last one is overwritten because the FileWriter you are passing to StatefulBeanToCsv is not in append mode. You should specify the append mode in the FileWriter constructor.

Writer writer = new FileWriter(filePath, true);
StatefulBeanToCsv beanToCsv = new StatefulBeanToCsvBuilder(writer).build();
beanToCsv.write(object);
writer.close();

Edit: This should solve multiple columns headers and column header not breaking line. It works for me.

public <T> void beanToCsvExport(final List<T> object, final String filePath) {
if (object == null) {
System.out.println("Initialization of object failed");
} else {
try {
cleanFile(filePath);
FileWriter writer = new FileWriter(filePath, true);
StatefulBeanToCsv beanToCsv = new StatefulBeanToCsvBuilder(writer).build();
beanToCsv.write(object);
writer.close();
} catch (IOException | CsvDataTypeMismatchException | CsvRequiredFieldEmptyException e) {
System.out.println(e.getMessage());
}
}
}

The function that resets the file:

public void cleanFile(String path) throws IOException {
FileWriter writer = new FileWriter(path); //Open as non-append to delete all data.
writer.write("");
writer.close();
}

To test the functions:

public void employeesToCsv() {
List<Employee> list = Arrays.asList(new Employee("101", "sunny"), new Employee("102", "Andrew"));
beanToCsvExport(list, "file.csv");
}

The achieved output:

"eId","eName"
"101","sunny"
"102","Andrew"


Related Topics



Leave a reply



Submit