使用 OpenCSV,如何使用 MappingStrategy 附加到现有 CSV?

With OpenCSV, how do I append to existing CSV using a MappingStrategy?

使用 OpenCSV,如何使用 MappingStrategy 附加到现有 CSV?我可以找到很多示例,其中不使用 Bean 映射策略,但我喜欢使用 bean 策略的列映射的动态特性,并希望以这种方式工作。这是我的代码,它只是将单行重写为 CSV 文件而不是附加。

我该如何解决这个问题?使用 OpenCSV 4.5。注意:我将 FileWriter 设置为 append=true 。这种情况并不像我预期的那样有效。 Re-running 此方法只会导致 over-writing 整个文件具有 header 和一行。

public void addRowToCSV(PerfMetric rowData) {
    File file = new File(PerfTestMetric.CSV_FILE_PATH);
    try {
        CSVWriter writer = new CSVWriter(new FileWriter(file, true));

        CustomCSVMappingStrategy<PerfMetric> mappingStrategy 
          = new CustomCSVMappingStrategy<>();
        mappingStrategy.setType(PerfMetric.class);

        StatefulBeanToCsv<PerfMetric> beanToCsv 
           = new StatefulBeanToCsvBuilder<PerfMetric>(writer)
            .withMappingStrategy(mappingStrategy)
            .withSeparator(',')
            .withApplyQuotesToAll(false)
            .build();

        try {
            beanToCsv.write(rowData);
        } catch (CsvDataTypeMismatchException e) {
            e.printStackTrace();
        } catch (CsvRequiredFieldEmptyException e) {
            e.printStackTrace();
        }
        writer.flush();
        writer.close();
    } catch (IOException e) {
            e.printStackTrace();
    }
}

或者,通常的模式是将所有行加载到列表中,然后 re-write 整个文件?我能够通过编写两个 MappingStrategy 映射策略然后有条件地将它们与 if-file-exists 一起使用来使其工作,但这样做会在我的代码中留下 "Unchecked assignment" 警告。不理想;希望有一个优雅的解决方案?

我从来没有找到这个问题的答案,所以我最终做的是在 .csv 文件存在与否的条件下做一个分支。如果文件存在,我使用 MappingStrategyWithoutHeader 策略,如果文件不存在,我使用 MappingStrategyWithHeader 策略。不理想,但我成功了。

我已将 OpenCSV 更新至 5.1 版并使其正常运行。在我的例子中,我需要 CSV headers 有一个特定的名称和位置,所以我同时使用 @CsvBindByName 和 @CsvBindByPosition,并且需要创建一个自定义 MappingStrategy 来让它工作。

后来,我需要编辑 MappingStrategy 以启用追加,因此当它处于追加模式时,我不需要生成 CSV header。

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

}

现在,如果您使用默认构造函数,它会将 header 添加到生成的 CSV 中,您可以使用布尔值告诉它添加 header 或忽略它。