我有一个包含以下列的 CSV 文件:id
, fname
, telephone
, lname
, address
.
我有一个Person
有id
,fname
和lname
数据成员的类。我只想将这些列映射到Person
CSV 文件中的对象并丢弃telephone
和address
列。我怎样才能做到这一点?随着将来添加更多列,该解决方案必须扩展。并且无论列位置如何都应该工作。
在理想的解决方案中,用户只会指定要读取的列,它应该可以正常工作。
您可以使用HeaderColumnNameTranslateMappingStrategy。为简单起见,让我们假设您的 CSV 具有以下列:Id
, Fname
, Telephone
, Lname
, 。Address
CsvToBean<Person> csvToBean = new CsvToBean<Person>();
Map<String, String> columnMapping = new HashMap<String, String>();
columnMapping.put("Id", "id");
columnMapping.put("Fname", "fname");
columnMapping.put("Lname", "lname");
HeaderColumnNameTranslateMappingStrategy<Person> strategy = new HeaderColumnNameTranslateMappingStrategy<Person>();
strategy.setType(Person.class);
strategy.setColumnMapping(columnMapping);
List<Person> list = null;
CSVReader reader = new CSVReader(new InputStreamReader(ClassLoader.getSystemResourceAsStream("test.csv")));
list = csvToBean.parse(strategy, reader);
columnMapping 将列与您的Person
对象映射。
OpenCSV 的最新版本弃用了该方法parse(X, Y)
,并重新开始使用 BeanBuilder,因此最佳答案已过时。
try {
CsvToBeanBuilder<PersonCSV> beanBuilder = new CsvToBeanBuilder<>(new InputStreamReader(new FileInputStream("your.csv")));
beanBuilder.withType(PersonCSV.class);
// build methods returns a list of Beans
beanBuilder.build().parse().forEach(e -> log.error(e.toString()));
} catch (FileNotFoundException e) {
log.error(e.getMessage(), e);
}
此方法允许您清理代码并删除 MappingStrategy(如果您喜欢意大利面条,您仍然可以使用它),因此您可以如下注释您的 CSV 类:
@CsvDate("dd/MM/yyyy hh:mm:ss")
@CsvBindByName(column = "Time Born", required = true)
private Date birthDate;
我不能代表 opencsv,但这很容易使用Super CSV实现,它有两个不同的阅读器,支持部分阅读(忽略列),以及读入 Javabean。CsvDozerBeanReader
甚至能够进行深度和基于索引的映射,因此您可以映射到嵌套字段。
我们(Super CSV 团队)刚刚发布了 2.0.0 版,可从 Maven 中心或 SourceForge 获得。
更新
这是一个示例(基于您创建的 GitHub 项目中的测试),它使用 Super CSV 而不是 opencsv。请注意,CSV 首选项需要surroundingSpacesNeedQuotes
启用标志,因为您的示例 CSV 文件无效(字段之间有空格 - 空格被视为 CSV 中数据的一部分)。
ICsvBeanReader beanReader = null;
try {
beanReader = new CsvBeanReader(
new InputStreamReader(
ClassLoader.getSystemResourceAsStream("test.csv")),
new CsvPreference.Builder(CsvPreference.STANDARD_PREFERENCE)
.surroundingSpacesNeedQuotes(true).build());
List<String> columnsToMap = Arrays.asList("fname", "telephone", "id");
// read the CSV header (and set any unwanted columns to null)
String[] header = beanReader.getHeader(true);
for (int i = 0; i < header.length; i++) {
if (!columnsToMap.contains(header[i])) {
header[i] = null;
}
}
Person person;
while ((person = beanReader.read(Person.class, header)) != null) {
System.out.println(person);
}
} finally {
beanReader.close();
}
使用uniVocity-parsers并完成它。列在输入 CSV 中的组织方式无关紧要,只会解析您需要的列。
如果写入,您在课堂上的列将被写入正确的列,而其他列将是空的。
这是一个带有一些示例的类:
class TestBean {
// if the value parsed in the quantity column is "?" or "-", it will be replaced by null.
@NullString(nulls = { "?", "-" })
// if a value resolves to null, it will be converted to the String "0".
@Parsed(defaultNullRead = "0")
private Integer quantity; // The attribute type defines which conversion will be executed when processing the value.
@Trim
@LowerCase
// the value for the comments attribute is in the column at index 4 (0 is the first column, so this means fifth column in the file)
@Parsed(index = 4)
private String comments;
// you can also explicitly give the name of a column in the file.
@Parsed(field = "amount")
private BigDecimal amount;
@Trim
@LowerCase
// values "no", "n" and "null" will be converted to false; values "yes" and "y" will be converted to true
@BooleanString(falseStrings = { "no", "n", "null" }, trueStrings = { "yes", "y" })
@Parsed
private Boolean pending;
}
这是获取列表的方法TestBean
BeanListProcessor<TestBean> rowProcessor = new BeanListProcessor<TestBean>(TestBean.class);
CsvParserSettings parserSettings = new CsvParserSettings();
parserSettings.setRowProcessor(rowProcessor);
parserSettings.setHeaderExtractionEnabled(true);
CsvParser parser = new CsvParser(parserSettings);
parser.parse(getReader("/examples/bean_test.csv"));
List<TestBean> beans = rowProcessor.getBeans();
披露:我是这个库的作者。它是开源和免费的(Apache V2.0 许可证)。
在不需要写入 csv 文件的字段上方使用 @CsvIgnore 注释。
使用 opencsv,您可以创建一个通用函数,例如:
public static <T> void csvWriterUtil(Class<T> beanClass, List<T> data, String outputFile, String[] columnMapping) {
try {
Writer writer = new BufferedWriter(new FileWriter(outputFile));
ColumnPositionMappingStrategy<T> strategy = new ColumnPositionMappingStrategy<>();
strategy.setType(beanClass);
strategy.setColumnMapping(columnMapping);
StatefulBeanToCsv<T> statefulBeanToCsv =new StatefulBeanToCsvBuilder<T>(writer)
.withMappingStrategy(strategy)
.build();
writer.write(String.join(",",columnMapping)+"\n");
statefulBeanToCsv.write(data);
writer.close();
} catch (IOException e) {
e.printStackTrace();
} catch (CsvRequiredFieldEmptyException e) {
e.printStackTrace();
} catch (CsvDataTypeMismatchException e) {
e.printStackTrace();
}
}
在这里,您只能通过 columnMapping 参数传递所需的列。
代码示例位于https://github.com/soumya-kole/JavaUtils/tree/master/CsvUtil
这是一个使用 OpenCSV 映射到 POJO 的好方法:
protected <T> List<T> mapToCSV(String csvContent, Class<T> mapToClass) {
CsvToBean<T> csvToBean = new CsvToBean<T>();
Map<String, String> columnMapping = new HashMap<>();
Arrays.stream(mapToClass.getDeclaredFields()).forEach(field -> {
columnMapping.put(field.getName(), field.getName());
});
HeaderColumnNameTranslateMappingStrategy<T> strategy = new HeaderColumnNameTranslateMappingStrategy<T>();
strategy.setType(mapToClass);
strategy.setColumnMapping(columnMapping);
CSVReader reader = new CSVReader(new StringReader(csvContent));
return csvToBean.parse(strategy, reader);
}
public static class MyPojo {
private String foo, bar;
public void setFoo(String foo) {
this.foo = foo;
}
public void setBar(String bar) {
this.bar = bar;
}
}
然后从您的测试中,您可以使用:
List<MyPojo> list = mapToCSV(csvContent, MyPojo.class);
SimpleFlatMapper可以使用 csv的标题或通过手动指定哪些列映射到哪些属性来轻松查看csv 入门。
CsvParser
.mapTo(MyObject.class)
.forEach(file, System.out::println);
看看 jcsvdao, https://github.com/eric-mckinley/jcsvdao/,使用休眠风格的映射文件,可以处理 1to1 和 1toMany 关系。如果您不拥有具有灵活匹配策略的 csv 文件,那就太好了。
jcvsdao 示例用法
示例用户 CSV 文件
Username, Email, Registration Date, Age, Premium User
Jimmy, jim@test.com, 04-05-2016, 15, Yes, M
Bob, bob@test.com, 15-01-2012, 32, No, M
Alice, alice@test.com, 22-09-2011, 24, No, F
Mike, mike@test.com, 11-03-2012, 18, Yes, M
Helen, helen@test.com, 02-12-2013, 22, Yes, F
Tom, tom@test.com, 08-11-2015, 45, No, M
创建一个 CSVDao
CSVDaoFactory factory = new CSVDaoFactory("/csv-config.xml");
CSVDao dao = new CSVDao(factory);
List<UserDetail> users = dao.find(UserDetail.class);
csv-config.xml
<CSVConfig>
<mappingFiles fileType="resource">
<mappingFile>/example01/mapping/UserDetail.csv.xml</mappingFile>
</mappingFiles>
</CSVConfig>
UserDetail.csv.xml
<CSVMapping className="org.jcsvdao.examples.example01.model.UserDetail" csvFile="csv-examples/example01/users.txt" delimiter="," ignoreFirstLine="true">
<matchAll/>
<properties>
<property index="0" property="username" primaryKey="true"/>
<property index="1" property="email"/>
<property index="2" property="registrationDate" converter="myDateConverter"/>
<property index="3" property="age"/>
<property index="4" property="premiumUser" converter="yesNoConverter"/>
<property index="5" property="gender" converter="myGenderConverter"/>
</properties>
<converters>
<dateConverter converterName="myDateConverter" format="dd-MM-yyyy"/>
<booleanConverter converterName="yesNoConverter" positive="Yes" negative="No"/>
<customConverter converterName="myGenderConverter" converterClass="org.jcsvdao.examples.example01.converter.GenderCustomerConverter"/>
</converters>
</CSVMapping>