我有一个包含以下列的CSV文件:id
,fname
,telephone
,lname
,address
。
我的Person
课程包含id
,fname
和lname
个数据成员。我想从CSV文件中仅将这些列映射到Person
对象,并放弃telephone
和address
列。我怎样才能做到这一点?解决方案必须随着未来添加更多列而扩展。无论列位置如何都应该工作。
在理想的解决方案中,用户只会指定要读取的列,它应该可以正常工作。
答案 0 :(得分:25)
您可以使用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
对象映射列。
答案 1 :(得分:4)
我不能代表opencsv,但使用Super CSV可以很容易地实现这一点,readers有两个不同的partial reading支持deep and index-based mapping(忽略列),以及读取Javabean的。 CsvDozerBeanReader
甚至可以{{3}},因此您可以映射到嵌套字段。
我们(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<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();
}
答案 2 :(得分:4)
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;
答案 3 :(得分:3)
使用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许可证)。
答案 4 :(得分:1)
这是一种使用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);
答案 5 :(得分:1)
使用opencsv,您可以创建一个通用函数,如:
public static <T> void csvWriterUtil(Class<T> beanClass, List<T> data, String outputFile, String[] columMapping){
try{
Writer writer = new BufferedWriter(new FileWriter(outputFile));
ColumnPositionMappingStrategy<T> strategy = new ColumnPositionMappingStrategy<>();
strategy.setType(beanClass);
strategy.setColumnMapping(columMapping);
StatefulBeanToCsv<T> statefulBeanToCsv =new StatefulBeanToCsvBuilder<T>(writer)
.withMappingStrategy(strategy)
.build();
writer.write(String.join(",",columMapping)+"\n");
statefulBeanToCsv.write(data);
writer.close();
} catch (IOException e) {
e.printStackTrace();
} catch (CsvRequiredFieldEmptyException e) {
e.printStackTrace();
} catch (CsvDataTypeMismatchException e) {
e.printStackTrace();
}
}
在这里,您只能通过columMapping参数传递必需的列。 https://github.com/soumya-kole/JavaUtils/tree/master/CsvUtil
中提供了代码示例答案 6 :(得分:0)
我已经实施了一个灵活的解决方案来解决这个问题。它的使用非常简单,我的github上提供了带代码的代码:
答案 7 :(得分:0)
最新版本的https://github.com/arnaudroger/SimpleFlatMapper 0.9.4 现在有一个CsvMapper。 它使用标头来匹配属性名称,或者如果没有标头,则可以通过构建器指定列名称。 它支持构造函数,setter和字段注入。从InputStream或Reader中读取。
public class MyParser {
private final CsvMapper<MyObject> 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"));
}
}
答案 8 :(得分:0)
看看jcsvdao,https://github.com/eric-mckinley/jcsvdao/,使用hibernate样式的映射文件,可以处理1to1和1toMany的关系。 如果您没有拥有csv文件,那就好了,因为它具有灵活的匹配策略。
答案 9 :(得分:0)
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>
答案 10 :(得分:0)
在不需要写入csv文件的字段上方使用@CsvIgnore注释。