OpenCSV – 如何将所选列映射到Java Bean而不管顺序如何?
我有一个包含以下列的CSV文件: id
, fname
, telephone
, lname
, address
。
我有一个带有id
, fname
和lname
数据成员的Person
类。 我想从CSV文件中仅将这些列映射到Person
对象,并丢弃telephone
和address
列。 我怎样才能做到这一点? 解决方案必须扩展,因为将来会添加更多列。 并且无论列位置如何都应该工作。
在理想的解决方案中,用户只会指定要读取的列,它应该可以正常工作。
您可以使用HeaderColumnNameTranslateMappingStrategy 。 让我们假设您的CSV具有以下列: Id
, Fname
, Telephone
, Lname
, Address
为简单起见。
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/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