Java OpenCSV:如何使用自定义列标题和自定义列位置从 POJO 创建 CSV 文件?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/45203867/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
OpenCSV: How to create CSV file from POJO with custom column headers and custom column positions?
提问by Vikram Pathania
I have created a MappingsBean class where all the columns of the CSV file are specified. Next I parse XML files and create a list of mappingbeans. Then I write that data into CSV file as report.
我创建了一个 MappingsBean 类,其中指定了 CSV 文件的所有列。接下来我解析 XML 文件并创建一个 mappingbeans 列表。然后我将该数据作为报告写入 CSV 文件。
I am using following annotations:
我正在使用以下注释:
public class MappingsBean {
@CsvBindByName(column = "TradeID")
@CsvBindByPosition(position = 0)
private String tradeId;
@CsvBindByName(column = "GWML GUID", required = true)
@CsvBindByPosition(position = 1)
private String gwmlGUID;
@CsvBindByName(column = "MXML GUID", required = true)
@CsvBindByPosition(position = 2)
private String mxmlGUID;
@CsvBindByName(column = "GWML File")
@CsvBindByPosition(position = 3)
private String gwmlFile;
@CsvBindByName(column = "MxML File")
@CsvBindByPosition(position = 4)
private String mxmlFile;
@CsvBindByName(column = "MxML Counterparty")
@CsvBindByPosition(position = 5)
private String mxmlCounterParty;
@CsvBindByName(column = "GWML Counterparty")
@CsvBindByPosition(position = 6)
private String gwmlCounterParty;
}
And then I use StatefulBeanToCsv
class to write into CSV file:
然后我使用StatefulBeanToCsv
class 写入 CSV 文件:
File reportFile = new File(reportOutputDir + "/" + REPORT_FILENAME);
Writer writer = new PrintWriter(reportFile);
StatefulBeanToCsv<MappingsBean> beanToCsv = new
StatefulBeanToCsvBuilder(writer).build();
beanToCsv.write(makeFinalMappingBeanList());
writer.close();
The problem with this approach is that if I use @CsvBindByPosition(position = 0)
to control
position then I am not able to generate column names. If I use @CsvBindByName(column = "TradeID")
then I am not able to set position of the columns.
这种方法的问题是,如果我@CsvBindByPosition(position = 0)
用来控制位置,那么我将无法生成列名。如果我使用,@CsvBindByName(column = "TradeID")
则无法设置列的位置。
Is there a way where I can use both annotations, so that I can create CSV files with column headers and also control column position?
有没有一种方法可以同时使用这两个注释,以便我可以创建带有列标题的 CSV 文件并控制列位置?
Regards, Vikram Pathania
问候, 维克拉姆·帕塔尼亚
回答by sebast26
I've had similar problem. AFAIK there is no build-in functionality in OpenCSV that will allow to write bean to CSV with custom column names andordering.
我有过类似的问题。AFAIK OpenCSV 中没有内置功能,可以使用自定义列名和顺序将 bean 写入 CSV 。
There are two main MappingStrategy
ies that are available in OpenCSV out of the box:
MappingStrategy
OpenCSV 中有两个主要的ie 可用:
HeaderColumnNameMappingStrategy
: that allows to map CVS file columns to bean fields based on custom name; when writing bean to CSV this allows to change column header name but we have no control on column orderColumnPositionMappingStrategy
: that allows to map CSV file columns to bean fields based on column ordering; when writing bean to CSV we can control column order but we get an empty header (implementation returnsnew String[0]
as a header)
HeaderColumnNameMappingStrategy
:允许根据自定义名称将 CVS 文件列映射到 bean 字段;将 bean 写入 CSV 时,这允许更改列标题名称,但我们无法控制列顺序ColumnPositionMappingStrategy
:允许根据列顺序将 CSV 文件列映射到 bean 字段;将 bean 写入 CSV 时,我们可以控制列顺序,但我们得到一个空标题(实现new String[0]
作为标题返回)
The only way I found to achieve both custom column names and ordering is to write your custom MappingStrategy
.
我发现实现自定义列名和排序的唯一方法是编写您的自定义MappingStrategy
.
First solution: fast and easy but hardcoded
第一个解决方案:快速简单但硬编码
Create custom MappingStrategy
:
创建自定义MappingStrategy
:
class CustomMappingStrategy<T> extends ColumnPositionMappingStrategy<T> {
private static final String[] HEADER = new String[]{"TradeID", "GWML GUID", "MXML GUID", "GWML File", "MxML File", "MxML Counterparty", "GWML Counterparty"};
@Override
public String[] generateHeader() {
return HEADER;
}
}
And use it in StatefulBeanToCsvBuilder
:
并将其用于StatefulBeanToCsvBuilder
:
final CustomMappingStrategy<MappingsBean> mappingStrategy = new CustomMappingStrategy<>();
mappingStrategy.setType(MappingsBean.class);
final StatefulBeanToCsv<MappingsBean> beanToCsv = new StatefulBeanToCsvBuilder<MappingsBean>(writer)
.withMappingStrategy(mappingStrategy)
.build();
beanToCsv.write(makeFinalMappingBeanList());
writer.close()
In MappingsBean
class we left CsvBindByPosition
annotations - to control ordering (in this solution CsvBindByName
annotations are not needed). Thanks to custom mapping strategy the header column names are included in resulting CSV file.
在MappingsBean
课堂上,我们留下了CsvBindByPosition
注释 - 控制排序(在这个解决方案CsvBindByName
中不需要注释)。由于自定义映射策略,标题列名称包含在生成的 CSV 文件中。
The downside of this solution is that when we change column ordering through CsvBindByPosition
annotation we have to manually change also HEADER
constant in our custom mapping strategy.
此解决方案的缺点是,当我们通过CsvBindByPosition
注释更改列顺序时,我们必须手动更改HEADER
自定义映射策略中的常量。
Second solution: more flexible
第二种解决方案:更灵活
The first solution works, but it was not good for me. Based on build-in implementations of MappingStrategy
I came up with yet another implementation:
第一个解决方案有效,但对我不利。基于内置的实现,MappingStrategy
我想出了另一个实现:
class CustomMappingStrategy<T> extends ColumnPositionMappingStrategy<T> {
@Override
public String[] generateHeader() {
final int numColumns = findMaxFieldIndex();
if (!isAnnotationDriven() || numColumns == -1) {
return super.generateHeader();
}
header = new String[numColumns + 1];
BeanField 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 beanField) {
if (beanField == null || beanField.getField() == null || beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class).length == 0) {
return StringUtils.EMPTY;
}
final CsvBindByName bindByNameAnnotation = beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class)[0];
return bindByNameAnnotation.column();
}
}
You can use this custom strategy in StatefulBeanToCsvBuilder
exactly this same as in the first solution (remember to invoke mappingStrategy.setType(MappingsBean.class);
, otherwise this solution will not work).
您可以StatefulBeanToCsvBuilder
按照与第一个解决方案完全相同的方式使用此自定义策略(请记住调用mappingStrategy.setType(MappingsBean.class);
,否则此解决方案将不起作用)。
Currently our MappingsBean
has to contain both CsvBindByName
and CsvBindByPosition
annotations. The first to give header column name and the second to create ordering of columns in the output CSV header. Now if we change (using annotations) either column name or ordering in MappingsBean
class - that change will be reflected in output CSV file.
目前我们MappingsBean
必须同时包含CsvBindByName
和CsvBindByPosition
注释。第一个给出标题列名称,第二个在输出 CSV 标题中创建列的顺序。现在,如果我们更改(使用注释)列名或MappingsBean
类中的排序- 该更改将反映在输出 CSV 文件中。
回答by akasha
If you dont have getDeclaredAnnotationsByType method, but need the name of your original field name:
如果您没有 getDeclaredAnnotationsByType 方法,但需要您的原始字段名称的名称:
beanField.getField().getName()
beanField.getField().getName()
public class CustomMappingStrategy<T> extends ColumnPositionMappingStrategy<T> {
@Override
public String[] generateHeader() {
final int numColumns = findMaxFieldIndex();
if (!isAnnotationDriven() || numColumns == -1) {
return super.generateHeader();
}
header = new String[numColumns + 1];
BeanField 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 beanField) {
if (beanField == null || beanField.getField() == null || beanField.getField().getDeclaredAnnotations().length == 0) {
return StringUtils.EMPTY;
}
return beanField.getField().getName();
}
}
}
回答by Lalji Gajera
Corrected above answer to match with newer version.
更正上述答案以匹配较新版本。
package csvpojo;
import org.apache.commons.lang3.StringUtils;
import com.opencsv.bean.BeanField;
import com.opencsv.bean.ColumnPositionMappingStrategy;
import com.opencsv.bean.CsvBindByName;
import com.opencsv.exceptions.CsvRequiredFieldEmptyException;
class CustomMappingStrategy<T> extends ColumnPositionMappingStrategy<T> {
@Override
public String[] generateHeader(T bean) throws CsvRequiredFieldEmptyException {
super.setColumnMapping(new String[ FieldUtils.getAllFields(bean.getClass()).length]);
final int numColumns = findMaxFieldIndex();
if (!isAnnotationDriven() || numColumns == -1) {
return super.generateHeader(bean);
}
String[] header = new String[numColumns + 1];
BeanField<T> 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> beanField) {
if (beanField == null || beanField.getField() == null
|| beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class).length == 0) {
return StringUtils.EMPTY;
}
final CsvBindByName bindByNameAnnotation = beanField.getField()
.getDeclaredAnnotationsByType(CsvBindByName.class)[0];
return bindByNameAnnotation.column();
}
}
Then call this to generate CSV. I have used Visitors as my POJO to populate, update wherever necessary.
然后调用它来生成 CSV。我已经使用访客作为我的 POJO 来填充,在必要时更新。
CustomMappingStrategy<Visitors> mappingStrategy = new CustomMappingStrategy<>();
mappingStrategy.setType(Visitors.class);
// writing sample
List<Visitors> beans2 = new ArrayList<Visitors>();
Visitors v = new Visitors();
v.set_1_firstName(" test1");
v.set_2_lastName("lastname1");
v.set_3_visitsToWebsite("876");
beans2.add(v);
v = new Visitors();
v.set_1_firstName(" firstsample2");
v.set_2_lastName("lastname2");
v.set_3_visitsToWebsite("777");
beans2.add(v);
Writer writer = new FileWriter("G://output.csv");
StatefulBeanToCsv<Visitors> beanToCsv = new StatefulBeanToCsvBuilder<Visitors>(writer)
.withMappingStrategy(mappingStrategy).withSeparator(',').withApplyQuotesToAll(false).build();
beanToCsv.write(beans2);
writer.close();
My bean annotations looks like this
我的 bean 注释看起来像这样
@CsvBindByName (column = "First Name", required = true)
@CsvBindByPosition(position=1)
private String firstName;
@CsvBindByName (column = "Last Name", required = true)
@CsvBindByPosition(position=0)
private String lastName;
回答by Rodrigo Broggi
thanks for this thread, it has been really useful for me... I've enhanced a little bit the provided solution in order to accept also POJO where some fields are not annotated (not meant to be read/written):
感谢这个线程,它对我真的很有用......我已经增强了一点提供的解决方案,以便也接受一些没有注释的字段(不打算读/写)的 POJO:
public class ColumnAndNameMappingStrategy<T> extends ColumnPositionMappingStrategy<T> {
@Override
public String[] generateHeader(T bean) throws CsvRequiredFieldEmptyException {
super.setColumnMapping(new String[ getAnnotatedFields(bean)]);
final int numColumns = getAnnotatedFields(bean);
final int totalFieldNum = findMaxFieldIndex();
if (!isAnnotationDriven() || numColumns == -1) {
return super.generateHeader(bean);
}
String[] header = new String[numColumns];
BeanField<T> beanField;
for (int i = 0; i <= totalFieldNum; i++) {
beanField = findField(i);
if (isFieldAnnotated(beanField.getField())) {
String columnHeaderName = extractHeaderName(beanField);
header[i] = columnHeaderName;
}
}
return header;
}
private int getAnnotatedFields(T bean) {
return (int) Arrays.stream(FieldUtils.getAllFields(bean.getClass()))
.filter(this::isFieldAnnotated)
.count();
}
private boolean isFieldAnnotated(Field f) {
return f.isAnnotationPresent(CsvBindByName.class) || f.isAnnotationPresent(CsvCustomBindByName.class);
}
private String extractHeaderName(final BeanField beanField) {
if (beanField == null || beanField.getField() == null) {
return StringUtils.EMPTY;
}
Field field = beanField.getField();
if (field.getDeclaredAnnotationsByType(CsvBindByName.class).length != 0) {
final CsvBindByName bindByNameAnnotation = field.getDeclaredAnnotationsByType(CsvBindByName.class)[0];
return bindByNameAnnotation.column();
}
if (field.getDeclaredAnnotationsByType(CsvCustomBindByName.class).length != 0) {
final CsvCustomBindByName bindByNameAnnotation = field.getDeclaredAnnotationsByType(CsvCustomBindByName.class)[0];
return bindByNameAnnotation.column();
}
return StringUtils.EMPTY;
}
}
}
回答by Anton Kumpan
I wanted to achieve bi-directional import/export - to be able to import generated CSV back to POJO and visa versa.
我想实现双向导入/导出 - 能够将生成的 CSV 导入回 POJO,反之亦然。
I was not able to use @CsvBindByPosition for this, because in this case - ColumnPositionMappingStrategy was selected automatically. Per documents: this strategy requires that the file does NOT have a header.
我无法为此使用 @CsvBindByPosition,因为在这种情况下 - ColumnPositionMappingStrategy 被自动选择。每个文档:此策略要求文件没有 header。
What I've used to achieve the goal:
我用来实现目标的方法:
HeaderColumnNameMappingStrategy
mappingStrategy.setColumnOrderOnWrite(Comparator<String> writeOrder)
CsvUtils to read/write csv
CsvUtils 读取/写入 csv
import com.opencsv.CSVWriter;
import com.opencsv.bean.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.util.List;
public class CsvUtils {
private CsvUtils() {
}
public static <T> String convertToCsv(List<T> entitiesList, MappingStrategy<T> mappingStrategy) throws Exception {
try (Writer writer = new StringWriter()) {
StatefulBeanToCsv<T> beanToCsv = new StatefulBeanToCsvBuilder<T>(writer)
.withMappingStrategy(mappingStrategy)
.withQuotechar(CSVWriter.NO_QUOTE_CHARACTER)
.build();
beanToCsv.write(entitiesList);
return writer.toString();
}
}
@SuppressWarnings("unchecked")
public static <T> List<T> convertFromCsv(MultipartFile file, Class clazz) throws IOException {
try (Reader reader = new BufferedReader(new InputStreamReader(file.getInputStream()))) {
CsvToBean<T> csvToBean = new CsvToBeanBuilder<T>(reader).withType(clazz).build();
return csvToBean.parse();
}
}
}
POJO for import/export
用于导入/导出的 POJO
public class LocalBusinessTrainingPairDTO {
//this is used for CSV columns ordering on exporting LocalBusinessTrainingPairs
public static final String[] FIELDS_ORDER = {"leftId", "leftName", "rightId", "rightName"};
@CsvBindByName(column = "leftId")
private int leftId;
@CsvBindByName(column = "leftName")
private String leftName;
@CsvBindByName(column = "rightId")
private int rightId;
@CsvBindByName(column = "rightName")
private String rightName;
// getters/setters omitted, do not forget to add them
}
Custom comparator for predefined String ordering:
用于预定义字符串排序的自定义比较器:
public class OrderedComparatorIgnoringCase implements Comparator<String> {
private List<String> predefinedOrder;
public OrderedComparatorIgnoringCase(String[] predefinedOrder) {
this.predefinedOrder = new ArrayList<>();
for (String item : predefinedOrder) {
this.predefinedOrder.add(item.toLowerCase());
}
}
@Override
public int compare(String o1, String o2) {
return predefinedOrder.indexOf(o1.toLowerCase()) - predefinedOrder.indexOf(o2.toLowerCase());
}
}
Ordered writing for POJO (answer to initial question)
POJO 的有序写作(对初始问题的回答)
public static void main(String[] args) throws Exception {
List<LocalBusinessTrainingPairDTO> localBusinessTrainingPairsDTO = new ArrayList<>();
LocalBusinessTrainingPairDTO localBusinessTrainingPairDTO = new LocalBusinessTrainingPairDTO();
localBusinessTrainingPairDTO.setLeftId(1);
localBusinessTrainingPairDTO.setLeftName("leftName");
localBusinessTrainingPairDTO.setRightId(2);
localBusinessTrainingPairDTO.setRightName("rightName");
localBusinessTrainingPairsDTO.add(localBusinessTrainingPairDTO);
//Creating HeaderColumnNameMappingStrategy
HeaderColumnNameMappingStrategy<LocalBusinessTrainingPairDTO> mappingStrategy = new HeaderColumnNameMappingStrategy<>();
mappingStrategy.setType(LocalBusinessTrainingPairDTO.class);
//Setting predefined order using String comparator
mappingStrategy.setColumnOrderOnWrite(new OrderedComparatorIgnoringCase(LocalBusinessTrainingPairDTO.FIELDS_ORDER));
String csv = convertToCsv(localBusinessTrainingPairsDTO, mappingStrategy);
System.out.println(csv);
}
Read exported CSV back to POJO (addition to original answer)
将导出的 CSV 读回 POJO(原始答案的补充)
Important: CSV can be unordered, as we are still using binding by name:
重要提示:CSV 可以是无序的,因为我们仍在使用按名称绑定:
public static void main(String[] args) throws Exception {
//omitted code from writing
String csv = convertToCsv(localBusinessTrainingPairsDTO, mappingStrategy);
//Exported CSV should be compatible for further import
File temp = File.createTempFile("tempTrainingPairs", ".csv");
temp.deleteOnExit();
BufferedWriter bw = new BufferedWriter(new FileWriter(temp));
bw.write(csv);
bw.close();
MultipartFile multipartFile = new MockMultipartFile("tempTrainingPairs.csv", new FileInputStream(temp));
List<LocalBusinessTrainingPairDTO> localBusinessTrainingPairDTOList = convertFromCsv(multipartFile, LocalBusinessTrainingPairDTO.class);
}
To conclude:
总结:
- We can read CSV to POJO, regardless of column order - because we are using @CsvBindByName
- We can control columns order on write using custom comparator
- 无论列顺序如何,我们都可以将 CSV 读取到 POJO - 因为我们使用的是 @CsvBindByName
- 我们可以使用自定义比较器控制写入时的列顺序
回答by Kalya Elisha
Try something like below:
尝试如下:
private static class CustomMappingStrategy<T> extends ColumnPositionMappingStrategy<T> {
String[] header;
public CustomMappingStrategy(String[] cols) {
header = cols;
}
@Override
public String[] generateHeader(T bean) throws CsvRequiredFieldEmptyException {
return header;
}
}
Then use it as follows:
然后按如下方式使用它:
String[] columns = new String[]{"Name", "Age", "Company", "Salary"};
CustomMappingStrategy<Employee> mappingStrategy = new CustomMappingStrategy<Employee>(columns);
Where columns are columns of your bean and Employee is your bean
其中列是您的 bean 的列,而 Employee 是您的 bean
回答by wrb
If you're only interested in sorting the CSV columns based on the order in which member variables appear in your model class (CsvRow
row in this example), then you can use a Comparator
implementation to solve this in a rather simple manner. Here's an example that does this in Kotlin:
如果您只对根据成员变量在模型类中出现的顺序(CsvRow
本示例中的行)对 CSV 列进行排序感兴趣,那么您可以使用Comparator
实现以相当简单的方式解决这个问题。这是在 Kotlin 中执行此操作的示例:
class ByMemberOrderCsvComparator : Comparator<String> {
private val memberOrder by lazy {
FieldUtils.getAllFields(CsvRow::class.java)
.map { it.getDeclaredAnnotation(CsvBindByName::class.java) }
.map { it?.column ?: "" }
.map { it.toUpperCase(Locale.US) } // OpenCSV UpperCases all headers, so we do this to match
}
override fun compare(field1: String?, field2: String?): Int {
return memberOrder.indexOf(field1) - memberOrder.indexOf(field2)
}
}
This Comparator
does the following:
这将Comparator
执行以下操作:
- Fetches each member variable field in our data class (
CsvRow
) - Finds all the ones with the
@CsvBindByName
annotation (in the order you specified them in theCsvRow
model) - Upper cases each to match the default OpenCsv implementation
- 获取数据类中的每个成员变量字段 (
CsvRow
) - 查找所有带
@CsvBindByName
注释的(按照您在CsvRow
模型中指定的顺序) - 大写每个匹配默认的 OpenCsv 实现
Next, apply this Comparator
to your MappingStrategy
, so it'll sort based off the specified order:
接下来,将其Comparator
应用于您的MappingStrategy
,因此它将根据指定的顺序进行排序:
val mappingStrategy = HeaderColumnNameMappingStrategy<OrderSummaryCsvRow>()
mappingStrategy.setColumnOrderOnWrite(ByMemberOrderCsvComparator())
mappingStrategy.type = CsvRow::class.java
mappingStrategy.setErrorLocale(Locale.US)
val csvWriter = StatefulBeanToCsvBuilder<OrderSummaryCsvRow>(writer)
.withMappingStrategy(mappingStrategy)
.build()
For reference, here's an example CsvRow
class (you'll want to replace this with your own model for your needs):
作为参考,这是一个示例CsvRow
类(您需要根据需要将其替换为您自己的模型):
data class CsvRow(
@CsvBindByName(column = "Column 1")
val column1: String,
@CsvBindByName(column = "Column 2")
val column2: String,
@CsvBindByName(column = "Column 3")
val column3: String,
// Other columns here ...
)
Which would produce a CSV as follows:
这将产生一个 CSV 如下:
"COLUMN 1","COLUMN 2","COLUMN 3",...
"value 1a","value 2a","value 3a",...
"value 1b","value 2b","value 3b",...
The benefit of this approach is that it removes the need to hard-code any of your column names, which should greatly simplify things if you ever need to add/remove columns.
这种方法的好处是不需要对任何列名进行硬编码,如果您需要添加/删除列,这将大大简化事情。
回答by Lex Luthor
I've improved on previous answers by removing all references to deprecated APIs while using the latest release of opencsv(4.6).
我在使用最新版本的opencsv(4.6) 时删除了对已弃用 API 的所有引用,从而改进了以前的答案。
A Generic Kotlin Solution
一个通用的 Kotlin 解决方案
/**
* Custom OpenCSV [ColumnPositionMappingStrategy] that allows for a header line to be generated from a target CSV
* bean model class using the following annotations when present:
* * [CsvBindByName]
* * [CsvCustomBindByName]
*/
class CustomMappingStrategy<T>(private val beanType: Class<T>) : ColumnPositionMappingStrategy<T>() {
init {
setType(beanType)
setColumnMapping(*getAnnotatedFields().map { it.extractHeaderName() }.toTypedArray())
}
override fun generateHeader(bean: T): Array<String> = columnMapping
private fun getAnnotatedFields() = beanType.declaredFields.filter { it.isAnnotatedByName() }.toList()
private fun Field.isAnnotatedByName() = isAnnotationPresent(CsvBindByName::class.java) || isAnnotationPresent(CsvCustomBindByName::class.java)
private fun Field.extractHeaderName() =
getAnnotation(CsvBindByName::class.java)?.column ?: getAnnotation(CsvCustomBindByName::class.java)?.column ?: EMPTY
}
Then use it as follows:
然后按如下方式使用它:
private fun csvBuilder(writer: Writer) =
StatefulBeanToCsvBuilder<MappingsBean>(writer)
.withSeparator(ICSVWriter.DEFAULT_SEPARATOR)
.withMappingStrategy(CustomMappingStrategy(MappingsBean::class.java))
.withApplyQuotesToAll(false)
.build()
// Kotlin try-with-resources construct
PrintWriter(File("$reportOutputDir/$REPORT_FILENAME")).use { writer ->
csvBuilder(writer).write(makeFinalMappingBeanList())
}
and for completeness, here's the CSV bean as a Kotlin data class:
为了完整起见,这里是作为 Kotlin 数据类的 CSV bean:
data class MappingsBean(
@field:CsvBindByName(column = "TradeID")
@field:CsvBindByPosition(position = 0, required = true)
private val tradeId: String,
@field:CsvBindByName(column = "GWML GUID", required = true)
@field:CsvBindByPosition(position = 1)
private val gwmlGUID: String,
@field:CsvBindByName(column = "MXML GUID", required = true)
@field:CsvBindByPosition(position = 2)
private val mxmlGUID: String,
@field:CsvBindByName(column = "GWML File")
@field:CsvBindByPosition(position = 3)
private val gwmlFile: String? = null,
@field:CsvBindByName(column = "MxML File")
@field:CsvBindByPosition(position = 4)
private val mxmlFile: String? = null,
@field:CsvBindByName(column = "MxML Counterparty")
@field:CsvBindByPosition(position = 5)
private val mxmlCounterParty: String? = null,
@field:CsvBindByName(column = "GWML Counterparty")
@field:CsvBindByPosition(position = 6)
private val gwmlCounterParty: String? = null
)
回答by Ravi
CustomMappingStrategy for generic class.
通用类的 CustomMappingStrategy。
public class CustomMappingStrategy<T> extends ColumnPositionMappingStrategy<T> {
@Override
public String[] generateHeader(T bean) throws CsvRequiredFieldEmptyException {
super.setColumnMapping(new String[ FieldUtils.getAllFields(bean.getClass()).length]);
final int numColumns = findMaxFieldIndex();
if (!isAnnotationDriven() || numColumns == -1) {
return super.generateHeader(bean);
}
String[] header = new String[numColumns + 1];
BeanField<T> 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> beanField) {
if (beanField == null || beanField.getField() == null
|| beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class).length == 0) {
return StringUtils.EMPTY;
}
final CsvBindByName bindByNameAnnotation = beanField.getField()
.getDeclaredAnnotationsByType(CsvBindByName.class)[0];
return bindByNameAnnotation.column();
}
}
POJO Class
POJO类
public class Customer{
@CsvBindByPosition(position=1)
@CsvBindByName(column="CUSTOMER", required = true)
private String customer;
}
Client Class
客户端类
List<T> data = getEmployeeRecord();
CustomMappingStrategy custom = new CustomMappingStrategy();
custom.setType(Employee.class);
StatefulBeanToCsv<T> writer = new StatefulBeanToCsvBuilder<T>(response.getWriter())
.withQuotechar(CSVWriter.NO_QUOTE_CHARACTER)
.withSeparator('|')
.withOrderedResults(false)
.withMappingStrategy(custom)
.build();
writer.write(reportData);
回答by Vladyslav Vynnyk
It is a solution for version greater than 4.3:
这是4.3以上版本的解决方案:
public class MappingBean {
@CsvBindByName(column = "column_a")
private String columnA;
@CsvBindByName(column = "column_b")
private String columnB;
@CsvBindByName(column = "column_c")
private String columnC;
// getters and setters
}
And use it as example:
并以它为例:
import org.apache.commons.collections4.comparators.FixedOrderComparator;
...
...
var mappingStrategy = new HeaderColumnNameMappingStrategy<MappingBean>();
mappingStrategy.setType(MappingBean.class);
mappingStrategy.setColumnOrderOnWrite(new FixedOrderComparator<>("COLUMN_C", "COLUMN_B", "COLUMN_A"));
var sbc = new StatefulBeanToCsvBuilder<MappingBean>(writer)
.withMappingStrategy(mappingStrategy)
.build();
Result:
结果:
column_c | column_b | column_a
列_c | 列_b | 列_a