web-dev-qa-db-fra.com

OpenCSV - Comment mapper les colonnes sélectionnées vers Java Bean indépendamment de leur ordre?

J'ai un fichier CSV avec les colonnes suivantes: id, fname, telephone, lname, address.

J'ai une classe Person avec des membres de données id, fname et lname. Je souhaite mapper uniquement ces colonnes sur l'objet Person à partir d'un fichier CSV et ignorer les colonnes telephone et address. Comment puis-je faire ceci? La solution doit évoluer à mesure que d'autres colonnes sont ajoutées à l'avenir. Et devrait fonctionner indépendamment de la position de la colonne. 

Dans une solution idéale, l’utilisateur ne spécifiera que les colonnes à lire et cela devrait fonctionner.

24
jsf

Vous pouvez utiliser HeaderColumnNameTranslateMappingStrategy . Supposons que votre CSV comporte les colonnes suivantes: Id, Fname, Telephone, Lname, Address par souci de simplicité. 

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 mappera les colonnes avec votre objet Person.

25
baskar_p

Les versions récentes d'OpenCSV déconseillent la méthode parse(X, Y) et il est recommandé d'utiliser BeanBuilder à la place, de sorte que la première réponse est obsolète.

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);
}

Cette méthode vous permet de nettoyer le code et de supprimer MappingStrategy (vous pouvez toujours l'utiliser si vous aimez les spaghettis) afin que vous puissiez annoter votre classe CSV comme suit:

@CsvDate("dd/MM/yyyy hh:mm:ss")
@CsvBindByName(column = "Time Born", required = true)
private Date birthDate;
4
agilob

Je ne peux pas parler pour opencsv, mais cela est facilement réalisable avec Super CSV , qui a deux lecteurs différents qui supportent lecture partielle (ignorant les colonnes), comme ainsi que la lecture dans un Javabéen. CsvDozerBeanReader est même capable de cartographie profonde et basée sur un index }, afin que vous puissiez mapper des champs imbriqués.

Nous (l'équipe de Super CSV) venons de publier la version 2.0.0, disponible à partir de Maven central ou de SourceForge. 

Mettre à jour

Voici un exemple (basé sur le test du projet GitHub que vous avez créé), qui utilise Super CSV au lieu d’opencsv. Notez que les préférences CSV exigent que l'indicateur surroundingSpacesNeedQuotes soit activé car votre exemple de fichier CSV n'est pas valide (il comporte des espaces entre les champs - les espaces sont considérés comme faisant partie des données en 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

Utilisez uniVocity-parsers et faites-le. Peu importe la manière dont les colonnes sont organisées dans le fichier CSV d'entrée, seules celles dont vous avez besoin seront analysées.

Si vous écrivez, les colonnes que vous avez en classe seront écrites dans les colonnes correctes, tandis que les autres seront vides.

Voici une classe avec quelques exemples:

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;
}

Voici comment obtenir une liste de 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();

Divulgation: Je suis l'auteur de cette bibliothèque. Il est open-source et gratuit (licence Apache V2.0).

3
Jeronimo Backes

Voici une bonne façon d'utiliser OpenCSV pour effectuer le mappage vers POJO de manière générique:

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;
    }
}

Ensuite, à partir de votre test, vous pouvez utiliser:

List<MyPojo> list = mapToCSV(csvContent, MyPojo.class);
1
Paul Hilliar

exemple d'utilisation de jcvsdao

Exemple de fichier CSV utilisateur

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

Créer un 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

la dernière version de https://github.com/arnaudroger/SimpleFlatMapper 0.9.4 a maintenant un CsvMapper. Il utilise l'en-tête pour faire correspondre le nom de la propriété ou, si aucun en-tête, vous pouvez spécifier le nom de la colonne via le générateur . Il prend en charge l'injection de constructeur, de définition et de champ. Lire depuis InputStream ou 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

Jetez un oeil à jcsvdao, https://github.com/eric-mckinley/jcsvdao/ , utilise des fichiers de mappage de style hibernation et peut gérer des relations 1to1 et 1toMany Bon si vous ne possédez pas les fichiers csv car dispose de stratégies de concordance flexibles.

0
lex404

J'ai mis en place une solution flexible pour résoudre ce problème. C'est très simple à utiliser et le code avec exemple est disponible sur mon github ci-dessous:

https://github.com/jsinghfoss/opencsv

0
jsf