Activité Lire un fichier XML et rechercher des objets dans une liste
Situation
Vous avez un fichier XML dont vous aimeriez utiliser le contenu dans un programme. Toutefois, vous vous rendez rapidement compte que l’interprétation de la syntaxe du XML n’est pas aussi simple que celle du CSV.
Heureusement, vous apprenez à la suite de quelques recherches, qu’il existe une recommandation du World Wide Web Consortium (W3C) qui définit une manière standard de lire et modifier un document XML à l’aide d’un langage de programmation (le Document Objet Model ou DOM) et que la bibliothèque standard de Java (Java Class Library ou JCL) contient une implémentation de cette recommandation.
Consigne
Pour ce travail, on vous demande de réaliser les tâches décrites ci-après.
Objectifs
À la fin de ce travail, vous devez :
- Connaître le fait que le XML ne peut pas être interprété aussi simplement que le CSV.
- Connaître l'existence du DOM pour lire et modifier des fichiers XML.
- Être capable d'utiliser un objet de type `ArrayList` en Java.
- Être capable d'effectuer une recherche dans une liste d'objets en Java.
Résultat attendu
Un compte rendu de l’activité et les réponses aux questions posées dans une section de votre rapport du module.
Ressources
Documents :
- Fichier de données : data.xml
Logiciel :
- NetBeans
Lire une liste d’adresses à partir d’un fichier XML
La syntaxe du XML ne peut pas être interprétée aussi facilement que le CSV, il est nécessaire pour cela d’utiliser une bibliothèque existante. Dans cet exemple, nous allons utiliser la bibliothèque standard de Java (JCL) pour lire le contenu d’un fichier XML qui contient une liste d’adresses dans un objet de type List de Person (List<Person>
).
Créez un nouveau projet ContactManager et copiez le code de la figure 1.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package ch.epaifribourg.ict.m100;
import java.io.File;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;
import org.w3c.dom.Element;
public class ContactManager {
private static class Person {
int id;
String firstname = "";
String lastname = "";
String street = "";
String postalCode = "";
String city = "";
String email = "";
}
public static void main(String[] args) throws Exception {
// Initialise une chaîne de caractère avec le chemin du fichier XML.
// Attention : Les backslashes (\) du chemin Windows sont dédoublés
// car dans une chaîne de caractères, le backslash est le caractère
// d’échappement qui permet d’écrire des caractères spéciaux tels que
// \r (carriage return), \n (line feed) ou \t (tab).
String filename = "C:\\Users\\frossardj\\Documents\\data.xml";
// Crée un objet de type File avec le chemin du fichier XML.
File xmlFile = new File(filename);
// Charge les données des personnes qui se trouve dans le fichier XML
// dans un objet de type List de Person et affecte une référence à cet
// objet à la variable personList.
List<Person> personList = loadPersonDataFromXml(xmlFile);
// Affiche le résultat dans la sortie standard (System.out).
printPersonList(System.out, personList);
}
/**
* Charge les données des personnes qui se trouvent dans le fichier XML dans un objet
* de type List de Person et renvoie l’objet.
*
* @param file le fichier XML à lire
* @return l’objet person correspondant.
*/
private static List<Person> loadPersonDataFromXml(File file) throws Exception {
// Crée un tableau dynamique.
List<Person> personList = new ArrayList<>();
// Crée un objet Document qui représente les données du fichier XML
// sous la forme d’une hiérarchie d’objets de type Node. Un objet de
// type nœud peut représenter aussi bien un élément, qu’un nœud
// de texte ou un attribut.
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(file);
doc.getDocumentElement().normalize();
// Recherche tous les elements <person>
NodeList personNodeList = doc.getElementsByTagName("person");
// Pour chaque élément XML de la liste
for (int i = 0; i < personNodeList.getLength(); i = i + 1) {
Node node = personNodeList.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
// Récupère l'élément
Element personElement = (Element)node;
// Crée un nouvel objet de type Person
Person person = new Person();
person.id = Integer.parseInt(personElement.getAttribute("id"));
person.firstname = personElement.getElementsByTagName("firstname").item(0).getTextContent();
person.lastname = personElement.getElementsByTagName("lastname").item(0).getTextContent();
person.postalCode = personElement.getElementsByTagName("postal-code").item(0).getTextContent();
person.street = personElement.getElementsByTagName("street").item(0).getTextContent();
person.city = personElement.getElementsByTagName("city").item(0).getTextContent();
person.email = personElement.getElementsByTagName("email").item(0).getTextContent();
// Ajoute la personne à la liste
personList.add(person);
}
}
// Renvoie la référence à l’objet personList
return personList;
}
/**
* Ecrit les élément d’une liste d'objet de type Person dans le flux de sortie
* passé en paramètre.
*
* @param out le flux de sortie
* @param personList la liste d'objet de type Person
*/
private static void printPersonList(PrintStream out, List<Person> personList) {
for (int i = 0; i < personList.size(); i = i + 1) {
Person person = personList.get(i);
printPerson(out, person);
}
}
/**
* Ecrit les données d’un objet de type Person dans le flux de sortie
* passé en paramètre.
*
* @param out le flux de sortie
* @param person l’objet de type Person
*/
private static void printPerson(PrintStream out, Person person) {
out.printf("(%d) %s %s, %s, %s %s\r\n",
person.id,
person.firstname,
person.lastname,
person.street,
person.postalCode,
person.city);
}
}
Exécutez votre programme dans NetBeans et assurez-vous qu’il fonctionne sans erreur. Demandez de l’aide, si vous ne parvenez pas à le faire fonctionner après avoir tout vérifié.
Rechercher des personnes selon un critère dans une liste de personnes
Au lieu d’afficher la liste de toutes les personnes, on aimerait maintenant permettre à l’utilisateur de saisir un nom (id) et afficher uniquement les données des personnes qui portent ce nom. De plus, on aimerait mesurer la durée la recherche et afficher cet durée à la suite des données des personnes trouvées.
Commencez par ajouter la fonction de la figure 2 dans votre code.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* Cherche dans une liste de personnes les personnes dont le nom est celui
* passé en paramètre et renvoie la liste des personnes trouvées.
* Si aucune personne n'a été trouvée, la fonction renvoie une liste vide.
*
* @param personList la liste de personnes
* @param name le nom de la personne recherchée
* @return l'objet correspondant à la personne
*/
private static List<Person> findPersonByName1(List<Person> personList, String name) {
// Crée un nouveau tableau dynamique pour y stocker les
// personnes qui correspondent au critère de recherche
List<Person> result = new ArrayList<>();
// Répète pour chaque personne de la liste.
for (int i = 0; i < personList.size(); i = i + 1) {
// Récupère l'objet de type Person.
Person person = personList.get(i);
// Teste si le nom de la personne correspond
// à celui passé en paramètre.
if (person.lastname.equals(name)) {
// Ajoute la personne à la liste des
// personnes trouvées.
result.add(person);
}
}
// Renvoie la liste des personnes trouvées.
return result;
}
Allez ensuite la procédure main
et supprimez l’appelle de la procédure printPersonList
et remplacez-le par le code nécessaire pour demander la valeur de l’identifiant à l’utilisateur en utilisant un objet de type Console
, recherchez la personne à l’aide de la fonction que vous venez d’ajouter (findPersonByName1
) et afficher les données de la personne avec la procédure printPerson
.
Essayez de réaliser cette modification par vous-même puis vérifiez qu’il correspond à celui de la figure 3.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
...
public static void main(String[] args) throws Exception {
...
// Demande à l'utilisateur de saisir le nom de la personne recherchée.
System.out.printf("Veuillez saisir le nom d’une person : ");
Console console = System.console();
String name = console.readLine();
// Recherche les personnes dans liste.
List<Person> result = findPersonByName1(personList, name);
// Affiche le résultat dans la sortie standard (System.out).
printPersonList(System.out, result);
}
...
Exécutez votre programme dans NetBeans une fois en indiquant le nom Alexander et une fois en indiquant le nom Walker. S’il fonctionne correctement, vous devriez obtenir les résultats suivant :
Il reste maintenant à mesurer le temps d’exécution de la fonction findPersonByName1
. Pour cela, on peut prendre le temps avant et après l’appel de la fonction avec la fonction System.nanoTime
et la différence de ces deux temps nous donne le temps d’exécution en ns.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
public static void main(String[] args) throws Exception {
...
// Demande à l'utilisateur de saisir le nom de la personne recherchée.
System.out.printf("Veuillez saisir le nom d’une person : ");
Console console = System.console();
String name = console.readLine();
// Mesure le temps avant l'exécution
long startTime = System.nanoTime();
// Recherche les personnes dans liste.
List<Person> result = findPersonByName1(personList, name);
// Mesure le temps après l'exécution
long endTime = System.nanoTime();
// Affiche le résultat dans la sortie standard (System.out).
printPersonList(System.out, result);
// Affiche le temps d'exécution
System.out.printf("\nTemps d'exécution : %d ns\n", endTime - startTime);
}
...
Exécuter plusieurs fois (5 ou 6 fois) le programme en indiquant le nom Alexander et notez à chaque fois le temps d’exécution. Calculer la moyenne des temps d’exécution pour le nom Alexander et faites la même expérience avec le nom Walker. Pour finir, refaites l’expérience avec le nom Skywalker.
Questions
- Le temps d’exécution pour une recherche dépend-il du nom que vous avez utilisé ?
- Que se passerait-il d’après vous, si on multipliait le nombre d’élément par 10 ou par 100 ? Expliquez votre raisonnement.
- Que pourrait-on faire pour diminuer le temps nécessaire à une recherche ? (Pensez à la manière dont vous recherchez un mot dans le dictionnaire)