1. Introduction

Dans ce document, nous allons voir comment charger des données depuis un fichier CSV simple à l'aide de la bibliothèque Open-CSVOpen-CSV (et MoteurCsv en annexe), puis transformer ces données en objets de notre modèle.

Open-CSV est une bibliothèque gratuite, écrite en Java, qui simplifie la lecture complexe des fichiers CSV.

1-A. À propos

Découvrir une technologie n'est pas chose facile. En aborder plusieurs d'un coup l'est encore moins. Partant de ce constat, cet article a été écrit pour aller à l'essentiel. Les points importants sont présentés dans le corps de l'article et les éléments secondaires sont expliqués en annexe.

1-B. Avant de commencer

Pour écrire ce tutoriel, j'ai utilisé les éléments suivants :

  • Java JDK 1.6.0_24-b07 ;
  • Eclipse Indigo 3.7 JEE 64b ;
  • Maven 3.0.3.

Ce tutoriel s'inspire très largement de mon article sur la lecture des fichiers CSV en JavaLes fichier CSV en Java. Ce tutoriel reprend également les objets utilisés dans les articles "Afficher un tableau avec un Table Model Swing en 5 minutes" et "Afficher un graphe jfreechart en 5 minutes". Même si elle est conseillée, la lecture de ces articles n'est pas indispensable pour la suite.

1-C. Mise-à-jour

27 février 2012 : Ajout d'un exemple d'utilisation de la lib MoteurCsv (en annexe).

12 juillet 2012 : Prise en compte de la nouvelle version de la lib MoteurCSV (en annexe).

2. Découverte du modèle d'exemple

2-A. Télécharger, installer et importer le projet d'exemple

Pour commencer, je vous propose de télécharger le fichier Zip "notes3_domain.zip", contenant un projet Java-Maven d'exemple.

Compilez le projet d'exemple et importez-le dans Eclipse (comme expliqué dans le tutoriel "Importer un projet Maven dans Eclipse en 5 minutes"Importer un projet Maven dans Eclipse en 5 minutes) ou dans l'IDE de votre choix.

Pour suivre ce tutoriel, vous pouvez vous contenter de lire les codes proposés ci-dessous (codes complets en annexe).

2-B. Les objets du modèle

Le projet d'exemple reprend les objets utilisés dans les tutoriels cités plus haut, et dont l'objectif est de modéliser (et d'afficher) les notes d'un groupe d'élèves au dernier examen.

Sans ordre, le modèle est composé des objets suivants.

Sexe.java
Sélectionnez

package com.thi.notes.domain;

public enum Sexe {

    HOMME("Garçon"),
    FEMME("Fille");

    private final String label;

    Sexe(String label) {
        this.label = label;
    }

    public String getLabel() {
        return label;
    }
}
Eleve.java
Sélectionnez

package com.thi.notes.domain;

public class Eleve {

    private String nom;
    private String prenom;
    private Integer annee;
    private Sexe sexe;

    public Eleve(String nom, String prenom, Integer annee, Sexe sexe) {
        ...
    }

    // + getter-setter
}
NoteEleve.java
Sélectionnez

package com.thi.notes.domain;

public class NoteEleve {

    private Eleve eleve;
    private Double note;

    public NoteEleve(Eleve eleve, Double note) {
        ...
    }

    // + getter-setter
}

Sans surprise, la classe Eleve représente une personne, et plus spécifiquement un élève. La classe NoteEleve est un "wrapper" qui possède un objet Eleve et une note (sur 20) associée. Quant à l'enum Sexe, son rôle dans l'histoire est évident.

2-C. Les données à lire

Dans ce tutoriel, nous allons utiliser deux fichiers CSV. Le premier, nommé "eleves.csv", contient la liste des élèves. Le second, nommé "notes-dernier-exam.csv", contient quant à lui les notes desdits élèves au dernier examen.

Dans un fichier CSV, les données sont séparées par des virgules, ou un point-virgule pour le format CSV français standard.

eleves.csv
Sélectionnez

# Nom;Prénom;Classe;Sexe;Date de naissance;Adresse

Durand;Marie;3;F;02/01/1992;15 rue du Lac 75001 Paris
Alesi;Julie;3;F;08/01/1992;72 av. Jean Dupont 75003 Paris
Martini;Carine;3;F;17/01/1992;2 rue du Moulin 92230 Neullavy
Varola;Sophie;3;F;21/01/1992;15 rue du Lac 75001 Paris
Labiche;Lelou;3;F;21/01/1992;15 rue du Lac 75001 Paris
Dujardin;Anne;3;F;03/02/1992;67 rue des Jardins 91800 Brunoy
Laventure;Martine;3;F;15/02/1992;15 rue du Lac 75001 Paris
...
Fivolini;Kevin;3;H;06/12/1992;15 rue du Lac 75001 Paris
Laferme;Martin;3;H;07/12/1992;15 rue du Lac 75001 Paris
Dupuis;Vincent;3;H;15/12/1992;15 rue du Lac 75001 Paris
Lagrange;Alexandre;3;H;28/12/1992;15 rue du Lac 75001 Paris

On constate que de nombreux élèves habitent au "15 rue du Lac". Cette adresse est celle de la résidence universitaire de l'école.

notes-dernier-exam.csv
Sélectionnez

# Nom;Prénom;Note

Durand;Marie;5
Alesi;Julie;8
Martini;Carine;10,5
Varola;Sophie;12
Labiche;Lelou;12
Dujardin;Anne;13
Laventure;Martine;14
...
Fivolini;Kevin;19
Laferme;Martin;20
Dupuis;Vincent;20

On remarque que "Alexandre Lagrange", que j'ai mis en fin de fichier pour simplifier la lecture, n'a pas de note au dernier examen ; il était absent. Dans ce tutoriel, nous dirons qu'une absence vaut un zéro. Il faudra donc gérer ce cas.

3. Action

3-A. Compléter le modèle

Durée estimée : 30 secondes.

Pour commencer, on doit ajouter les attributs "date de naissance" et "adresse", qui ne sont pas encore dans "Eleve.java" et modifier le constructeur de la classe en conséquence.

Nouveaux attributs dans Eleve.java
Sélectionnez

public class Eleve {

    private String nom;
    private String prenom;
    private Integer annee;
    private Sexe sexe;
    private Date dateNaissance;
    private String adresse;

    public Eleve(String nom, String prenom, Integer annee, Sexe sexe, Date dateNaissance, String adresse) {
        ...
    }

    // + getter-setter
    ...
}

3-B. Créer des DAO vides

Durée estimée : 1 minute.

Nous allons créer deux DAO vides : un pour charger la liste des élèves et l'autre pour charger les notes. Chaque DAO ne renvoie qu'une partie des informations disponibles. Nous verrons dans un prochain tutoriel comment réconcilier les données, ce qui n'est pas le sujet ici.

NoteDao.java
Sélectionnez

package com.thi.notes.dao;

import java.util.Map;

public class NoteDao {

    public Map<String, Double> findNotesDernierExam() {
        return null;
    }
}

Ce DAO (Data Access Object) renvoie une "Map" dont les clés sont les noms (prénom + nom) des élèves et les valeurs sont les notes obtenues au dernier examen.

EleveDao.java
Sélectionnez

package com.thi.notes.dao;

import java.util.List;

import com.thi.notes.domain.Eleve;

public class EleveDao {

    public List<Eleve> findEleves() {
        return null;
    }
}

Ce DAO renvoie la liste des élèves, tout simplement, en utilisant les objets Eleve et Sexe. Pour lire les données à l'aide de EleveDao, on utilisera un code ressemblant au suivant.

Utilisation de EleveDao
Sélectionnez

final EleveDao eleveDao = new EleveDao();
final List<Eleve> eleves = eleveDao.findEleves();

for(Eleve eleve : eleves) {
    System.out.println(eleve);
}

Dans la suite de ce tutoriel, nous allons nous concentrer sur "EleveDao.java", l'autre DAO utilisant globalement les mêmes principes. Un code d'exemple est toutefois proposé en annexe et dans le fichier Zip final.

3-C. Ajout d'Open-CSV

Durée estimée : 30 secondes.

Pour utiliser Open CSV dans un projet Java-Maven, il suffit d'ajouter une dépendance dans le "pom.xml".

Dépendance vers Open CSV
Sélectionnez

<dependency>
    <groupId>net.sf.opencsv</groupId>
    <artifactId>opencsv</artifactId>
    <version>2.3</version>
</dependency>

Puis, comme d'habitude, il faut relancer une installation Maven.

Maven
Sélectionnez

mvn clean install eclipse:eclipse

En fonction des éléments déjà présents sur l'ordinateur, la réponse de Maven devrait ressembler à la trace suivante.

Maven
Sélectionnez

C:\dvp\notes>mvn clean install eclipse:eclipse

[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Notes 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.4.1:clean (default-clean) @ notes ---
[INFO] Deleting C:\dvp\notes\target
[INFO]
[INFO] --- maven-resources-plugin:2.4.3:resources (default-resources) @ notes ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 2 resources
[INFO]
[INFO] --- maven-compiler-plugin:2.3.2:compile (default-compile) @ notes ---
[INFO] Compiling 9 source files to C:\dvp\notes\target\classes
[INFO]
[INFO] --- maven-resources-plugin:2.4.3:testResources (default-testResources) @ notes ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 0 resource

...

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 10.197s
[INFO] Finished at: Wed Jan 18 20:19:12 CET 2012
[INFO] Final Memory: 12M/59M

3-D. Lecture des données

Durée estimée : 3 minutes.

On entre dans le vif du sujet. Comme indiqué plus haut, nous allons nous concentrer sur la lecture de la liste d'élèves.

Pour commencer, il faut accéder au fichier CSV.

Utilisation du helper
Sélectionnez

public class EleveDao {

    private final static String RESOURCES_PATH = "src/main/resources/";
    private final static String ELEVES_FILE_NAME = "eleves.csv";

    public List<Eleve> findEleves() {

        File file = new File(RESOURCES_PATH + ELEVES_FILE_NAME);
        FileReader fr = new FileReader(file);

        ...
    }
}

Puis j'utilise l'objet "CSVReader" fourni par la bibliothèque "Open-CSV".

Utilisation du helper, sans la gestion des exceptions
Sélectionnez

public class EleveDao {

    private final static char SEPARATOR = ';';
    ...

    public List<Eleve> findEleves() {
        File file = new File(RESOURCES_PATH + ELEVES_FILE_NAME);
        FileReader fr = new FileReader(file);

        CSVReader csvReader = new CSVReader(fr, SEPARATOR);
        ...

Ce "reader" nous permet de lire les données du fichier.

Lire les données
Sélectionnez

List<String[] > data = new ArrayList<String[] >();

String[] nextLine = null;
while ((nextLine = csvReader.readNext()) != null) {
    int size = nextLine.length;

    // ligne vide
    if (size == 0) {
        continue;
    }
    String debut = nextLine[0].trim();
    if (debut.length() == 0 && size == 1) {
        continue;
    }

    // ligne de commentaire
    if (debut.startsWith("#")) {
        continue;
    }
    data.add(nextLine);
}

Il ne reste plus qu'à transformer les données, qu'on a sous forme de tableau de String, en objets Eleve.

Transformer les données en objets
Sélectionnez

List<String[] > data = new ArrayList<String[] >();
...

List<Eleve> eleves = new ArrayList<Eleve>();

DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
for (String[] oneData : data) {
    String nom = oneData[0];
    String prenom = oneData[1];
    String classeStr = oneData[2];
    String sexeStr = oneData[3];
    String dateNaissanceStr = oneData[4];
    String adresse = oneData[5];

    Integer classe = Integer.parseInt(classeStr);
    Sexe sexe = (sexeStr.equalsIgnoreCase("F")) ? FEMME : HOMME;
    Date dateNaissance = dateFormat.parse(dateNaissanceStr);

    Eleve eleve = new Eleve(nom, prenom, classe, sexe, dateNaissance, adresse);
    eleves.add(eleve);
}

return eleves;

Ici, la seule petite difficulté vient de la transformation de la date de naissance, d'un String vers un objet Date, ce que l'on fait à l'aide des classes Java "DateFormat" et "SimpleDateFormat".

Transformer de la date de naissance
Sélectionnez

DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");

String dateNaissanceStr = "28/01/1992"; // Par exemple
Date dateNaissance = dateFormat.parse(dateNaissanceStr);

Le tutoriel "fichiers CSV en Java"Les fichier CSV en Java propose une façon plus élégante de transformer les lignes du fichier en liste d'objets.

Il ne reste plus qu'à réorganiser un peu le code (pour éviter d'avoir des méthodes trop grosses) et à factoriser les traitements (et ainsi ne pas dupliquer de code dans l'autre DAO). Le code final, avec ces changements, est disponible en annexe.

4. Conclusions

Comme on vient de le constater, il est relativement simple de lire un fichier CSV à l'aide d'une bibliothèque telle que Open-CSV. Cette dernière réalise un ensemble de traitements complexes et en masque la complexité. Il existe d'autres bibliothèques sur le marché, qui fonctionnent différemment et que je vous invite à regarder, au moins pas curiosité.

Et bien entendu, je vous encourage à lire mon tutoriel sur les fichiers CSV en JavaLes fichier CSV en Java.

Le code final de ce tutoriel est disponible dans le fichier ZIP notes4_domain-et-dao-csv.zip.code final

Retrouvez les tutoriels de la série "en 5 minutes" sur developpez.com à l'adresse http://thierry-leriche-dessirier.developpez.com/#page_articlesEn 5 minutes.

5. Remerciements

Je tiens à remercier, en tant qu'auteur de tutoriel rapide, toutes les personnes qui m'ont aidé et soutenu. Je pense tout d'abord à mes collègues qui subissent mes questions au quotidien mais aussi à mes contacts et amis du Web, dans le domaine de l'informatique ou non, qui m'ont fait part de leurs remarques et critiques. Bien entendu, je n'oublie pas l'équipe de developpez.com qui m'a guidé dans la rédaction de cet article et m'a aidé à le corriger et le faire évoluer, principalement sur le forum.

Plus particulièrement j'adresse mes remerciements, à Claude LELOUP (ClaudeLELOUP), Mickael BARON (keulkeul), Nemek et Yan BONNEL.

Image non disponible

6. Annexes

6-A. Liens

Le site Web d'Open-CSV est disponible à l'adresse
http://opencsv.sourceforge.net

Pour suivre cet article, je conseille la lecture des tutoriels
"Importer un projet Maven dans Eclipse en 5 minutes" disponible à l'adresse
http://thierry-leriche-dessirier.developpez.com/tutoriels/java/importer-projet-maven-dans-eclipse-5-minImporter un projet Maven dans Eclipse en 5 minutes

Je vous encourage à lire mon tutoriel sur les fichiers CSV en Java, disponible à l'adresse
http://thierry-leriche-dessirier.developpez.com/tutoriels/java/csv-avec-javaLes fichier CSV en Java

6-B. Les fichiers importants en entier

Le helper CsvFileHelper.java
Sélectionnez

package com.thi.notes.dao;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import au.com.bytecode.opencsv.CSVReader;

public class CsvFileHelper {

    public static List<String[] > readCsvFile(String fileName, char separator) {
        final List<String[] > data = new ArrayList<String[] >();

        try {
            final File file = new File(fileName);
            final FileReader fr = new FileReader(file);

            final CSVReader csvReader = new CSVReader(fr, separator);

            String[] nextLine = null;
            while ((nextLine = csvReader.readNext()) != null) {
                final int size = nextLine.length;

                // ligne vide
                if (size == 0) {
                    continue;
                }
                final String debut = nextLine[0].trim();
                if (debut.length() == 0 && size == 1) {
                    continue;
                }

                // ligne de commentaire
                if (debut.startsWith("#")) {
                    continue;
                }
                data.add(nextLine);
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return data;
    }
}
Interface EleveDao.java
Sélectionnez

package com.thi.notes.dao;

import java.util.List;

import com.thi.notes.domain.Eleve;

public interface EleveDao {

    List<Eleve> findEleves();
}
Implémentation CsvEleveDao.java
Sélectionnez

package com.thi.notes.dao;

import static com.thi.notes.dao.CsvFileHelper.readCsvFile;
import static com.thi.notes.domain.Sexe.FEMME;
import static com.thi.notes.domain.Sexe.HOMME;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.thi.notes.domain.Eleve;
import com.thi.notes.domain.Sexe;

public class CsvEleveDao implements EleveDao {

    private final static char SEPARATOR = ';';
    private final static String RESOURCES_PATH = "src/main/resources/";
    private final static String ELEVES_FILE_NAME = "eleves.csv";

    @Override
    public List<Eleve> findEleves() {

        final List<String[] > data = readCsvFile(RESOURCES_PATH + ELEVES_FILE_NAME, SEPARATOR);

        final List<Eleve> eleves = dataToEleves(data);

        return eleves;
    }

    private List<Eleve> dataToEleves(List<String[] > data) {
        final List<Eleve> eleves = new ArrayList<Eleve>();

        try {
            final DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
            for (String[] oneData : data) {
                final String nom = oneData[0];
                final String prenom = oneData[1];
                final String classeStr = oneData[2];
                final String sexeStr = oneData[3];
                final String dateNaissanceStr = oneData[4];
                final String adresse = oneData[5];

                final Integer classe = Integer.parseInt(classeStr);
                final Sexe sexe = (sexeStr.equalsIgnoreCase("F")) ? FEMME : HOMME;
                final Date dateNaissance = dateFormat.parse(dateNaissanceStr);

                final Eleve eleve = new Eleve(nom, prenom, classe, sexe, dateNaissance, adresse);
                eleves.add(eleve);
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }

        return eleves;
    }
}

Dans un prochain tutoriel, je vous montrerai comment charger la liste d'élèves à partir d'une base de données MySql, et non plus à partir d'un fichier CSV. Néanmoins, la signature de la méthode findEleves ne devrait pas changer. C'est pourquoi j'ai ajouté une interface. Comme vous pouvez le deviner, de la même manière que CsvEleveDao implémente EleveDao, le prochain tutoriel introduira l'implémentation DatabaseEleveDao de EleveDao pour lire les données depuis la base.

Interface NoteDao.java
Sélectionnez

package com.thi.notes.dao;

import java.util.Map;

public interface NoteDao {

     Map<String, Double> findNotesDernierExam();
}
Implémentation CsvNoteDao.java
Sélectionnez

package com.thi.notes.dao;

import static com.thi.notes.dao.CsvFileHelper.readCsvFile;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class CsvNoteDao implements NoteDao {

    private final static char SEPARATOR = ';';
    private final static String RESOURCES_PATH = "src/main/resources/";
    private final static String ELEVES_FILE_NAME = "notes-dernier-exam.csv";

    @Override
    public Map<String, Double> findNotesDernierExam() {

        final List<String[] > data = readCsvFile(RESOURCES_PATH + ELEVES_FILE_NAME, SEPARATOR);

        final Map<String, Double> map = dataToMap(data);

        return map;
    }

    private Map<String, Double> dataToMap(List<String[] > data) {
        final Map<String, Double> map = new HashMap<String, Double>();

        for (String[] oneData : data) {

            final String nom = oneData[0];
            final String prenom = oneData[1];

            final String noteStr = oneData[2];
            final Double note = new Double(noteStr.replace(",", "."));

            map.put(nom + " " + prenom, note);
        }

        return map;
    }
}
DaoLauncher.java pour vérifier que ça marche
Sélectionnez

package com.thi.notes;

import java.util.List;
import java.util.Map;

import com.thi.notes.dao.CsvEleveDao;
import com.thi.notes.dao.CsvNoteDao;
import com.thi.notes.dao.EleveDao;
import com.thi.notes.dao.NoteDao;
import com.thi.notes.domain.Eleve;

public class DaoLauncher {

    public static void main(String[] args) {

        final EleveDao eleveDao = new CsvEleveDao();
        final List<Eleve> eleves = eleveDao.findEleves();

        System.out.println("Liste des eleves");
        for (Eleve eleve : eleves) {
            System.out.println(eleve);
        }

        final NoteDao noteDao = new CsvNoteDao();
        final Map<String, Double> notes = noteDao.findNotesDernierExam();
        System.out.println("Notes des eleves");
        for (String key : notes.keySet()) {
            final Double note = notes.get(key);
            System.out.println(key + " : " + note);
        }
    }
}

6-C. Utilisation de la lib MoteurCsv

Comme me l'a proposé Yan BonnelBlog de Yan Bonnel, j'ai adapté le code présenté ci-dessus pour employer la bibliothèque MoteurCsvPage du projet MoteurCsv, qui ressemble un peu à JPA dans sont utilisation.

Tout d'abord, je change mon pom.xml, sachant que MoteurCsv possède déjà une dépendance vers OpenCSV.

Extrait du fichier pom.xml utilisant MoteurCsv
Sélectionnez

<!-- MoteurCSV -->
<dependency>
	<groupId>fr.ybo</groupId>
	<artifactId>moteurcsv</artifactId>
	<version>1.1.3</version>
</dependency>
pom.xml utilisant MoteurCsv
Sélectionnez

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.thi</groupId>
	<artifactId>notes-avec-moteurcsv</artifactId>
	<version>1.0-SNAPSHOT</version>
	<packaging>jar</packaging>

	...

	<developers>
		<!-- Thierry -->
		<developer><name>Thierry Leriche-Dessirier</name></developer>

		<!-- Yan -->
		<developer><name>Yan Bonnel</name></developer>
	</developers>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<junit.version>4.8.2</junit.version>
		<jfreechart.version>1.0.14</jfreechart.version>
		
		<!-- Remplacement de opencsv par moteurcsv -->
		<!--<opencsv.version>2.3</opencsv.version>-->
		<moteurcsv.version>1.1.3</moteurcsv.version>
	</properties>

	<dependencyManagement>
		<dependencies>
			...

			<!-- Open CSV -->
			<!--<dependency>
				<groupId>net.sf.opencsv</groupId>
				<artifactId>opencsv</artifactId>
				<version>${opencsv.version}</version>
			</dependency>-->
			
			<!-- MoteurCSV -->
			<dependency>
				<groupId>fr.ybo</groupId>
				<artifactId>moteurcsv</artifactId>
				<version>${moteurcsv.version}</version>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<dependencies>
		...

		<!-- Open CSV -->
		<!--<dependency>
			<groupId>net.sf.opencsv</groupId>
			<artifactId>opencsv</artifactId>
		</dependency>-->
		
		<!-- MoteurCSV -->
		<dependency>
			<groupId>fr.ybo</groupId>
			<artifactId>moteurcsv</artifactId>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.3.2</version>
				<configuration>
					<encoding>UTF-8</encoding>
					<source>1.6</source>
					<target>1.6</target>
				</configuration>
			</plugin>
		</plugins>
	</build>

	<repositories>
		<repository>
			<id>ybonnel-release</id>
			<url>https://repository-ybonnel.forge.cloudbees.com/release/</url>
		</repository>
	</repositories>

</project>

Je vais ensuite modifier mes beans pour que ça ressemble à du JPA.

Eleve.java
Sélectionnez

@FichierCsv(separateur = ";")
public class Eleve {

	// private String nom;
	// private String prenom;
	// private Integer annee;
	// private Sexe sexe;
	// private Date dateNaissance;
	// private String adresse;

	@BaliseCsv("Nom")
	private String nom;
	@BaliseCsv("Prénom")
	private String prenom;
	@BaliseCsv(value = "Classe", adapter = AdapterInteger.class)
	private Integer annee;
	@BaliseCsv(value = "Sexe", adapter = AdapterSexe.class)
	private Sexe sexe;
	@BaliseCsv(value = "Date de naissance", adapter = AdapterDate.class)
	private Date dateNaissance;
	@BaliseCsv(value = "Adresse")
	private String adresse;

	public Eleve() {
		// Constructeur par defaut obligatoire pour MoteurCsv.
	}
	
	...
Sexe.java
Sélectionnez

public enum Sexe {
	
	HOMME("Garçon"),
	FEMME("Fille");

	private final String label;

	Sexe(String label) {
		this.label = label;
	}

	public String getLabel() {
		return label;
	}

	public static class AdapterSexe implements AdapterCsv<Sexe> {

		@Override
		public Sexe parse(String chaine) {
			return (chaine.equalsIgnoreCase("F")) ? FEMME : HOMME;
		}

		@Override
		public String toString(Sexe objet) {
			return objet == FEMME ? "F" : "H";
		}
	}

}

Je peux maintenant passer à la lecture du fichier.

CsvEleveDao.java
Sélectionnez

public class CsvEleveDao implements EleveDao {

	private final static char SEPARATOR = ';';
	private final static String RESOURCES_PATH = "src/main/resources/";
	private final static String ELEVES_FILE_NAME = "eleves.csv";

	// Moteur CSV
	private final static MoteurCsv moteurCsv = new MoteurCsv(Eleve.class);

	@Override
	public List<Eleve> findEleves() {

		// final List<String[] > data = readCsvFile(RESOURCES_PATH +
		// ELEVES_FILE_NAME, SEPARATOR);
		// final List<Eleve> eleves = dataToEleves(data);

		List<Eleve> eleves = null;
		try {
			eleves = moteurCsv.parseInputStream(new FileInputStream(new File(RESOURCES_PATH + ELEVES_FILE_NAME)), Eleve.class);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			eleves = new ArrayList<Eleve>();
		}

		return eleves;
	}
	
	public static class AdapterDate implements AdapterCsv<Date> {

		final DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");

		@Override
		public Date parse(String chaine) {
			try {
				return dateFormat.parse(chaine);
			} catch (ParseException e) {
				return null;
			}
		}

		@Override
		public String toString(Date objet) {
			return dateFormat.format(objet);
		}

	}

Ici, je ne m'occupe que de la liste des élèves. Pour lire l'autre fichier, il faut faire la même chose.

Petit détail qui compte, MoteurCsv a besoin, par défaut, que la première ligne importante contienne les noms des champs, puisqu'ils sont utilisés pour "mapper" les objets.

eleves.csv
Sélectionnez

Nom;Prénom;Classe;Sexe;Date de naissance;Adresse

Durand;Marie;3;F;02/01/1992;15 rue du Lac 75001 Paris
Alesi;Julie;3;F;08/01/1992;72 av. Jean Dupont 75003 Paris
Martini;Carine;3;F;17/01/1992;2 rue du Moulin 92230 Neullavy
...

Et voilà... C'est encore plus simple.

Le code final de ce tutoriel, incluant l'utilisation de MoteurCsv est disponible dans le fichier ZIP notes4_moteurcsv.zip.code final

6-C-1. Version 1.2.0 de la lib

Mise-à-jour : 12 juillet 2012.

Les nouvelles version de la lib MoteurCsv (par exemple la version 1.2.0) s'utilisent d'une autre manière que ce qui est présenté ci-dessus. La méthode "parseInputStream()" renvoie désormais un objet de type "Resultat" et non plus la liste des objets lus.

Objet Resultat
Sélectionnez

public class CsvEleveDao implements EleveDao {

	private final static char SEPARATOR = ';';
	private final static String RESOURCES_PATH = "src/main/resources/";
	private final static String ELEVES_FILE_NAME = "eleves.csv";

	// Moteur CSV
	private final static MoteurCsv moteurCsv = new MoteurCsv(Eleve.class);

	@Override
	public List<Eleve> findEleves() {
		
		Resultat<Eleve> resultat =  moteurCsv.parseInputStream(new FileInputStream(new File(RESOURCES_PATH + ELEVES_FILE_NAME)), Eleve.class);
		List<Eleve> eleves = resultat.getObjets();

Cet objet "Resultat" propose la méthode "getErreurs()" qui montre les éventuelles erreurs qui sont arrivées durant les traitements.

En plus de ce changement, la lib propose une nouvelle syntaxe (prog fonc) qui fera un peu penser à celle utilisée dans Guava. Elle permet de traiter les éléments trouvés dirrectement.

parseFileAndInsert
Sélectionnez

public class CsvEleveDao implements EleveDao {

	final MoteurCsv moteurCsv = new MoteurCsv(Eleve.class);
	...

	@Override
	public List<Eleve> findEleves() {
	
		final File f = new File(RESOURCES_PATH + ELEVES_FILE_NAME);
		final FileReader fr = new FileReader(f);
		
		final List<Eleve> eleves = newArrayList(); // Static factory Guava
		moteurCsv.parseFileAndInsert(fr, Eleve.class, new InsertObject<Eleve>() {

			@Override
			public void insertObject(Eleve eleve) {
				foo(eleve); // Faire un truc sur l'eleve.
				...
				result.add(eleve); // Mettre dans la liste, si besoin.
				...
				System.out.println(eleve); // Un petit sysout pour le plaisir.
			}
		});