Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Appending to CSV file without headers

Tags:

java

opencsv

I am using opencsv to write a Java bean to a CSV file with headers. The file name contains the current date. If the user runs it for a second time in the same day, it appends to the file but adds another header line.

How do I append to the file but without the column headers.

    public class CSVExport {

final File USERHOME = new File(System.getProperty("user.home"));
final List<saleExport> listSaleExport = new ArrayList<>();
final ObjectMapper mapper = new ObjectMapper();

public void createCsvFile(String Region, Map<String, String> currentSale) throws IOException {

    mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
    // use column position mapping strategy for no headers?
    LocalDate today = LocalDate.now();

                final SaleExport saleExport = mapper.convertValue(currentSale, SaleExport.class);
                listSaleExport.add(saleExport);
                writeToFile(today +" LA.csv", listSaleExport);
}

public void writeToFile(String filename, List<listSaleExport> listSaleExport) throws IOException {

    File file = new File(PROCESSED_DIR +"\\", "filename");

    if (!file.exists()) {
        try {
            Writer writer = new FileWriter(PROCESSED_DIR +"\\" +filename, true);
            StatefulBeanToCsvBuilder<listSaleExport> beanToCsv = new StatefulBeanToCsvBuilder<>(writer);
            StatefulBeanToCsv<listSaleExport> beanWriter = beanToCsv.build();
            beanWriter.write(listSaleExport);
            writer.close();
        } catch (Exception ex) {
            System.out.println("Error : " + ex.getMessage());
        }
    } else {
        try {
            Writer writer = new FileWriter(PROCESSED_DIR +"\\" +"filename");
            StatefulBeanToCsvBuilder<listSaleExport> beanToCsv = new StatefulBeanToCsvBuilder<>(writer);
            StatefulBeanToCsv<listSaleExport> beanWriter = beanToCsv.build();
            beanWriter.write(listSaleExport);
            writer.close();
        } catch (Exception ex) {
            System.out.println("Error : " + ex.getMessage());
        }
    }
  }
}
like image 676
Radika Moonesinghe Avatar asked Feb 22 '18 08:02

Radika Moonesinghe


People also ask

Is it necessary to have header line in CSV?

From those guidelines and giving the lack of standardization, the header line is optional in a CSV file. When present, the header line must be the first line in the file and must contain the same number of fields as the records. Header and records lines must use the same field delimiters.

How do I skip a header in CSV?

To read CSV file without header, use the header parameter and set it to “None” in the read_csv() method.

Can you append to a CSV file?

If you need to append row(s) to a CSV file, replace the write mode ( w ) with append mode ( a ) and skip writing the column names as a row ( writer.

How do I remove a header from a CSV file in Excel?

In the List properties, under General > Column Titles, change the setting to 'Hide". Then go to under General > Column Titles, change the setting to 'Hide". or simply select the header (by going to ancestor select list column title style ) and box type none or cut it.


2 Answers

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.

:)

like image 63
Scott Conway Avatar answered Sep 20 '22 16:09

Scott Conway


I don't know if the feature was finally added in OpenCSV. Right now the latest version is 5.1 and I haven't read anything about it...

As I've posted in another question, I got it to work after updating to 5.1:

Needed to have CSVs with both specific field headers (@CsvBindByName) and positions (@CsvBindByPosition), so I already had to make my own MappingStrategy.

public class CustomMappingStrategy<T> extends ColumnPositionMappingStrategy<T> {
    private boolean useHeader=true;
    
    public CustomMappingStrategy(){
    }
    
    public CustomMappingStrategy(boolean useHeader) {
        this.useHeader = useHeader;
    }
    
    @Override
    public String[] generateHeader(T bean) throws CsvRequiredFieldEmptyException {
        final int numColumns = FieldUtils.getAllFields(bean.getClass()).length;
        super.setColumnMapping(new String[numColumns]);
        
        if (numColumns == -1) {
            return super.generateHeader(bean);
        }
        
        String[] header = new String[numColumns];
        
        if(!useHeader){
            return ArrayUtils.EMPTY_STRING_ARRAY;
        }
        BeanField<T, Integer> beanField;
        for (int i = 0; i < numColumns; i++){
            beanField = findField(i);
            String columnHeaderName = extractHeaderName(beanField);
            header[i] = columnHeaderName;
        }
        
        return header;
    }
    
    private String extractHeaderName(final BeanField<T, Integer> beanField){
        if (beanField == null || beanField.getField() == null || beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class).length == 0){
            return StringUtils.EMPTY;
        }
        
        //return value of CsvBindByName annotation
        final CsvBindByName bindByNameAnnotation = beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class)[0];
        return bindByNameAnnotation.column();
    }

}

I also made a generic exporter class, to ease CSV generation:

public class ExportGenericCSV<T> {
    private static final Logger logger = LoggerFactory.getLogger(ExportGenericCSV.class);
    private final char DEFAULT_QUOTE_CHARACTER = CSVWriter.NO_QUOTE_CHARACTER;
    private final char CSV_SEPARATOR = CSVWriter.DEFAULT_SEPARATOR;
    
    public void writeToFile(String fileName, List<T> content){
        writeList(fileName, content, true);
    }
    
    public void appendToFile(String fileName, List<T> content){
        writeList(fileName, content, false, StandardOpenOption.APPEND);
    }

    @SuppressWarnings("unchecked")
    private void writeList(String fileName, List<T> exports, boolean addHeader, OpenOption...openOptions){
        if(exports == null  || exports.isEmpty()) return;
        try (Writer writer = Files.newBufferedWriter(Paths.get(fileName), openOptions)){

            CustomMappingStrategy<T> mapping = new CustomMappingStrategy<T>(addHeader);
            mapping.setType((Class<? extends T>) exports.get(0).getClass());
            
            StatefulBeanToCsv<T> beanToCsv = new StatefulBeanToCsvBuilder<T>(writer)
                    .withQuotechar(DEFAULT_QUOTE_CHARACTER)
                    .withSeparator(CSV_SEPARATOR)
                    .withMappingStrategy(mapping)
                    .build();

            beanToCsv.write(exports);
            
            writer.flush();
            writer.close();
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
        } catch (CsvDataTypeMismatchException e) {
            logger.error(e.getMessage(), e);
        } catch (CsvRequiredFieldEmptyException e) {
            logger.error(e.getMessage(), e);
        }
    }
}

This way, when you need to export a list of items you can use:

ExportGenericCSV<ExportedClass> exporter = new ExportGenericCSV<ExportedClass>();

and then use exporter.writeToFile(...) or exporter.appendToFile(...)

like image 44
afarrapeira Avatar answered Sep 20 '22 16:09

afarrapeira