30

我有一个包含以下列的 CSV 文件:id, fname, telephone, lname, address.

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

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

4

10 回答 10

28

您可以使用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对象映射。

于 2013-02-20T09:44:27.313 回答
8

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;
于 2018-01-12T13:41:36.997 回答
4

我不能代表 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();
}
于 2012-11-23T05:46:46.420 回答
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 许可证)。

于 2015-05-05T09:22:52.567 回答
1

在不需要写入 csv 文件的字段上方使用 @CsvIgnore 注释。

于 2020-11-11T05:15:15.380 回答
1

使用 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

于 2020-09-24T16:54:12.633 回答
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);
于 2016-04-01T14:35:18.610 回答
0

SimpleFlatMapper可以使用 csv的标题或通过手动指定哪些列映射到哪些属性来轻松查看csv 入门。

CsvParser
    .mapTo(MyObject.class)
    .forEach(file, System.out::println);
于 2014-09-26T09:11:59.707 回答
0

看看 jcsvdao, https://github.com/eric-mckinley/jcsvdao/,使用休眠风格的映射文件,可以处理 1to1 和 1toMany 关系。如果您不拥有具有灵活匹配策略的 csv 文件,那就太好了。

于 2016-02-25T22:27:57.917 回答
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>
于 2016-03-05T12:35:27.783 回答