OpenCSV – 如何将所选列映射到Java Bean而不管顺序如何?

我有一个包含以下列的CSV文件: idfnametelephonelnameaddress

我有一个带有idfnamelname数据成员的Person类。 我想从CSV文件中仅将这些列映射到Person对象,并丢弃telephoneaddress列。 我怎样才能做到这一点? 解决方案必须扩展,因为将来会添加更多列。 并且无论列位置如何都应该工作。

在理想的解决方案中,用户只会指定要读取的列,它应该可以正常工作。

您可以使用HeaderColumnNameTranslateMappingStrategy 。 让我们假设您的CSV具有以下列: IdFnameTelephoneLnameAddress为简单起见。

 CsvToBean csvToBean = new CsvToBean(); Map columnMapping = new HashMap(); columnMapping.put("Id", "id"); columnMapping.put("Fname", "fname"); columnMapping.put("Lname", "lname"); HeaderColumnNameTranslateMappingStrategy strategy = new HeaderColumnNameTranslateMappingStrategy(); strategy.setType(Person.class); strategy.setColumnMapping(columnMapping); List list = null; CSVReader reader = new CSVReader(new InputStreamReader(ClassLoader.getSystemResourceAsStream("test.csv"))); list = csvToBean.parse(strategy, reader); 

columnMapping将使用Person对象映射列。

我不能代表opencsv,但使用Super CSV可以很容易地实现这一点, Super CSV有两个不同的阅读器 ,支持部分阅读 (忽略列),以及读入Javabean。 CsvDozerBeanReader甚至能够进行深度和基于索引的映射 ,因此您可以映射到嵌套字段。

我们(Super CSV团队)刚刚发布了2.0.0版本,可以从Maven central或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 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解析器并完成它。 如何在输入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列表的TestBean

 BeanListProcessor rowProcessor = new BeanListProcessor(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 beans = rowProcessor.getBeans(); 

披露:我是这个图书馆的作者。 它是开源和免费的(Apache V2.0许可证)。

最新版本的OpenCSV不赞成使用方法parse(X, Y) ,而是重新使用BeanBuilder,所以最常见的答案是过时的。

 try { CsvToBeanBuilder 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(如果您喜欢spaghetti,您仍然可以使用它),因此您可以按如下方式注释CSV类:

 @CsvDate("dd/MM/yyyy hh:mm:ss") @CsvBindByName(column = "Time Born", required = true) private Date birthDate; 

这是一个很好的方法来使用OpenCSV来一般地映射到POJO:

 protected  List mapToCSV(String csvContent, Class mapToClass) { CsvToBean csvToBean = new CsvToBean(); Map columnMapping = new HashMap<>(); Arrays.stream(mapToClass.getDeclaredFields()).forEach(field -> { columnMapping.put(field.getName(), field.getName()); }); HeaderColumnNameTranslateMappingStrategy strategy = new HeaderColumnNameTranslateMappingStrategy(); 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 list = mapToCSV(csvContent, MyPojo.class); 

我已经实施了一个灵活的解决方案来解决这个问题。 它的使用非常简单,我的github上提供了带代码的代码:

https://github.com/jsinghfoss/opencsv

最新版本的https://github.com/arnaudroger/SimpleFlatMapper 0.9.4现在有一个CsvMapper。 它使用标头来匹配属性名称,或者如果没有标题,则可以通过构建器指定列名称。 它支持构造函数,setter和字段注入。 从InputStream或Reader读取。

 public class MyParser { private final CsvMapper mapper = CsvMapperFactory.newInstance().newMapper(MyObject.class); public void writeAllObjectToLambda(Writer writer, InputStream is) throws IOException { mapper.forEach(is, (o) -> writer.append(o.toString()).append("\n")); } } 

看看jcsvdao, https://github.com/eric-mckinley/jcsvdao/ ,使用hibernate样式的映射文件,可以处理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 users = dao.find(UserDetail.class); 

CSV-config.xml中

   /example01/mapping/UserDetail.csv.xml   

UserDetail.csv.xml