web-dev-qa-db-ja.com

OpenCSV-選択した列をJava順序に関係なくBeanにマッピングする方法は?

次の列を持つCSVファイルがあります:idfnametelephonelnameaddress

Personidfnameのデータメンバーを持つlnameクラスがあります。これらの列のみをCSVファイルのPersonオブジェクトにマップし、telephone列とaddress列を破棄したいです。これどうやってするの?ソリューションは、将来さらに列が追加されるにつれて拡張する必要があります。列の位置に関係なく動作するはずです。

理想的なソリューションでは、ユーザーは読み取る列のみを指定し、動作するはずです。

25
jsf

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オブジェクトにマップします。

26
baskar_p

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;
5
agilob

Opencsvについて話すことはできませんが、これは Super CSV を使用して簡単に達成できます。2つの異なる readers をサポートします partial reading (列を無視)、およびJavabeanへの読み込み。 CsvDozerBeanReaderディープおよびインデックスベースのマッピング にも対応しているため、ネストされたフィールドにマッピングできます。

私たち(Super CSVチーム)は、バージョン2.0.0をリリースしました。これは、Maven centralまたはSourceForgeから入手できます。

更新

以下に、opencsvの代わりにSuper CSVを使用する(作成したGitHubプロジェクトのテストに基づく)例があります。 CSV設定では、サンプルCSVファイルが無効であるため、surroundingSpacesNeedQuotesフラグを有効にする必要があります(フィールド間にスペースがあります-スペースは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();
}
4
James Bassett

niVocity-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ライセンス)。

3
Jeronimo Backes

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);
1
Paul Hilliar

jcvsdaoの使用例

サンプルユーザーCSVファイル

Username, Email, Registration Date, Age, Premium User
Jimmy, [email protected], 04-05-2016, 15, Yes, M
Bob, [email protected], 15-01-2012, 32, No, M
Alice, [email protected], 22-09-2011, 24, No, F
Mike, [email protected], 11-03-2012, 18, Yes, M
Helen, [email protected], 02-12-2013, 22, Yes, F
Tom, [email protected], 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>
0
lex404

https://github.com/arnaudroger/SimpleFlatMapper 0.9.4の最新バージョンにCsvMapperが追加されました。ヘッダーを使用してプロパティ名と照合します。ヘッダーがない場合は、ビルダーで列名を指定できます。コンストラクター、セッター、およびフィールド注入をサポートします。 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"));
    }
}
0
user3996996

Jcsvdaoをご覧ください https://github.com/eric-mckinley/jcsvdao/ は、休止状態のスタイルマッピングファイルを使用し、1to1および1toManyの関係を処理できます。柔軟なマッチング戦略があるため、csvファイルを所有していない場合に適しています。

0
lex404

この問題に取り組むための柔軟なソリューションを実装しました。使い方は非常に簡単で、以下の私のgithubで例のコードを入手できます。

https://github.com/jsinghfoss/opencsv

0
jsf