使用 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 或忽略它。
使用 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 或忽略它。