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

时间:2012-11-22 03:54:14

标签: java csv opencsv supercsv

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

我的Person课程包含idfnamelname个数据成员。我想从CSV文件中仅将这些列映射到Person对象,并放弃telephoneaddress列。我怎样才能做到这一点?解决方案必须随着未来添加更多列而扩展。无论列位置如何都应该工作。

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

11 个答案:

答案 0 :(得分:25)

您可以使用HeaderColumnNameTranslateMappingStrategy。让我们假设您的CSV包含以下列:IdFnameTelephoneLnameAddress,以简化起见。

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上提供了带代码的代码:

https://github.com/jsinghfoss/opencsv

答案 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注释。