I. Introduction▲
DOM : Document Object Model. Il s'agit d'une API permettant la manipulation de fichiers XML. Grâce à cette API, nous allons pouvoir lire un fichier XML, mais également ajouter de nouveaux éléments à ce fichier. DOM peut-être vu comme une arborescence de fichier. Il faut également savoir qu'avec l'API dom de python, tout est élément ou nœud, même le texte. Ainsi, si le TAG <A> contient « Bonjour » cela voudra dire que le nœud A de type ELEMENT contient le nœud « Bonjour » de type TEXT. Mais tout ceci sera plus clair avec le code (du moins je l'espère).
Dans ce tutoriel, nous allons simplement lire un fichier pour transformer les TAG en objets python que nous aurons définis. Nous aurons donc un XML avec des TAG Personnes qui auront des nom, prénom et adresse. Ces informations seront injectées dans les objets python appropriés que nous pourrons par la suite manipuler. Pour l'instant, nous nous arrêterons là. Par la suite j'ajouterai la manière d'enregistrer les modifications.
Il est important que vous connaissiez les bases du XML et de Python. Il vous faut la dernière version de python ainsi que le module PyXML.
II. Le fichier XML▲
<
quil>
<
personne>
<
nom>
Durant</
nom>
<
prenom>
Stephan</
prenom>
<
adresse>
<
ville>
Bruxelles</
ville>
</
adresse>
</
personne>
<
personne>
<
nom>
Dupont</
nom>
<
prenom>
Henri</
prenom>
<
adresse/>
</
personne>
</
quil>
Ce fichier XML représente un petit carnet d'adresses personnel. Évidemment, il est peu fourni en informations, car il va nous servir à réaliser notre petit exercice. Vous pourrez, comme exercice, tenter d'ajouter de nouveaux éléments qui l'étofferont.
Nous avons un nœud (ou TAG XML) principal s'appelant « quil » (ben oui ! pourquoi pas !). Il ne peut y avoir qu'un seul élément à ce niveau de hiérarchie du fichier XML. On peut voir un celui-ci comme un disque dur. Ainsi le premier élément est l'équivalent de la racine de votre disque. Les autres nœuds imbriqués correspondront à des répertoires et sous répertoires et sous répertoires et ainsi de suite. Sauvegardez ce fichier sous « personnes.xml » avec l'éditeur de votre choix.
Mais au fait, pourquoi choisir XML plutôt qu'un fichier plat ou encore une base de données ?
La réponse, vous la trouverez sur le net au travers de sites spécialisés dans les comparatifs. Moi tout ce que je peux faire à ce sujet est donné mon humble avis. XML permet d'avoir une structure clairement définie ce qui lui donne un avantage par rapport aux fichiers plats. Maintenant par rapport aux SGBD, le principal apport est la facilité d'utilisation du XML, mais également la portabilité de l'application ainsi créée. On pourra voyager d'un PC à l'autre avec son application et ses données. Pas besoin de réseau. Une clef USB qui contiendra le soft complet avec des données continuellement à jour. Cela, un SGBD ne le permet pas de manière simple. La contrepartie est la sécurité et la gestion des données s'il s'agit d'une application avec beaucoup de lien entre données. C'est à vous de peser le pour et le contre et de faire votre choix.
III. Lecture du XML et création des objets▲
III-A. Le script python complet▲
class
Personne:
nom =
None
prenom =
None
adresse =
Adresse
(
)
def
__init__
(
self):
pass
class
Adresse:
ville =
None
def
__init__
(
self):
pass
class
TransformXmlToPersonnes:
__currentNode__ =
None
__personneList__ =
None
def
__init__
(
self):
self.readXml
(
)
def
readXml
(
self):
from
xml.dom.minidom import
parse
self.doc =
parse
(
'E:/python/samplexml/personnes.xml'
)
def
getRootElement
(
self):
if
self.__currentNode__ ==
None
:
self.__currentNode__ =
self.doc.documentElement
return
self.__currentNode__
def
getPersonnes
(
self):
if
self.__personneList__ !=
None
:
return
self.__personneList__ =
[]
for
personnes in
self.getRootElement
(
).getElementsByTagName
(
"personne"
):
if
personnes.nodeType ==
personnes.ELEMENT_NODE:
p =
Personne
(
)
try
:
p.nom =
self.getText
(
personnes.getElementsByTagName
(
"nom"
)[0
])
p.prenom =
self.getText
(
personnes.getElementsByTagName
(
"prenom"
)[0
])
p.adresse =
self.getAdresse
(
personnes.getElementsByTagName
(
"adresse"
)[0
])
except
:
print
'Un des TAGS suivants est manquant : nom, prenom, adresse'
self.__personneList__.append
(
p)
return
self.__personneList__
def
getAdresse
(
self, node):
adress =
Adresse
(
)
try
:
adress.ville =
self.getText
(
node.getElementsByTagName
(
"ville"
)[0
])
except
:
adress.ville =
None
return
adress
def
getText
(
self, node):
return
node.childNodes[0
].nodeValue
if
__name__
==
"__main__"
:
x=
TransformXmlToPersonnes
(
)
print
x.getPersonnes
(
)[1
].nom
Vous pouvez enregistrer ce script sous « personnes.py ». Nous allons décortiquer ce script pas à pas avec la description des différentes classes et méthodes.
III-B. La classe Personne▲
class
Personne:
nom =
None
prenom =
None
adresse =
Adresse
(
)
def
__init__
(
self):
pass
Cette classe va nous servir à créer des objets de type personnes qui vont contenir toutes les informations recueillies dans le fichier XML pour une personne. L'attribut Adresse est une classe décrite ci-après. La méthode « __init__ » est le constructeur de la classe qui ne fait rien.
III-C. La classe Adresse▲
class
Adresse:
ville =
None
def
__init__
(
self):
pass
Cette ne contient qu'un attribut qui est la ville où habite la personne. Vous pourrez améliorer les informations par vous-même par la suite.
III-D. La classe TransformXmlToPersonnes▲
class
TransformXmlToPersonnes:
__currentNode__ =
None
__personneList__ =
None
def
__init__
(
self):
self.readXml
(
)
def
readXml
(
self):
from
xml.dom.minidom import
parse
self.doc =
parse
(
'personnes.xml'
)
Il s'agit de la classe qui va permettre le traitement du fichier XML. La méthode « __init__ » fait appel à la méthode « readXML » qui va lire le fichier XML grâce à la méthode « parse » importée depuis « xml.dom.minidom ». Cette méthode prend en argument le nom du fichier XML à traiter, c'est-à-dire à transformer en un objet compréhensible pour python. Ci-après nous allons étudier les autres méthodes de notre classe.
def
getRootElement
(
self):
if
self.__currentNode__ ==
None
:
self.__currentNode__ =
self.doc.documentElement
return
self.__currentNode__
On regarde si on a déjà lu le premier élément du fichier (dans notre cas « quil »). Si oui, on ne fait que retourner l'attribut __currentNode__, sinon, on prend le premier élément du document. On appelle cette façon de travailler une « lazy instanciation ». On va par la suite travailler à partir de ce premier élément comme s'il s'agissait d'un répertoire de base (C: sous win ou / sous Un*x par exemple). On va ainsi descendre de plus en plus bas dans notre hiérarchie de nœud.
def
getPersonnes
(
self):
if
self.__personneList__ !=
None
:
return
self.__personneList__
self.__personneList__ =
[]
for
personnes in
self.getRootElement
(
).getElementsByTagName
(
"personne"
):
if
personnes.nodeType ==
personnes.ELEMENT_NODE:
p =
Personne
(
)
try
:
p.nom =
self.getText
(
personnes.getElementsByTagName
(
"nom"
)[0
])
p.prenom =
self.getText
(
personnes.getElementsByTagName
(
"prenom"
)[0
])
p.adresse =
self.getAdresse
(
personnes.getElementsByTagName
(
"adresse"
)[0
])
self.__personneList__.append
(
p)
except
:
print
'Un des TAGS suivants est manquant : nom, prenom, adresse'
return
self.__personneList__
Il s'agit de la méthode la plus complexe de notre exercice, car il va s'agir de transformer les nœuds personnes en objet Personne. Comme il existe plusieurs personnes, nous allons créer une liste dans laquelle nous allons stocker chaque objet Personne créé. En premier lieu nous regardons si la liste a déjà été créée et si oui on retourne l'objet déjà existant. Sinon, on crée une nouvelle liste vide.
Ensuite on crée une boucle for sur tous les éléments avec comme nom « personne » contenus dans l'élément root (ici : « quil »). Pour chacun de ces éléments, on regarde s'il s'agit d'un type de nœud ELEMENT. Si oui, on crée un nouvel objet Personne et on tente de récupérer les valeurs des nœuds nom, prenom et adresse en utilisant les méthodes « getText » et « getAdresse ». Dans ce cas précis, comme je sais qu'il n'y a qu'un nom par personne, je lui demande l'élément 0. Effectivement la méthode « getElementsByTagName » renvoie une liste. Si un des nœuds n'existe pas dans le fichier, j'imprime une erreur. On peut améliorer cela en renvoyant une exception particulière. On assigne à chaque variable la valeur appropriée et on ajoute l'objet p à notre liste que l'on retourne lorsque l'itération est terminée.
def
getAdresse
(
self, node):
adress =
Adresse
(
)
try
:
adress.ville =
self.getText
(
node.getElementsByTagName
(
"ville"
)[0
])
except
:
adress.ville =
None
return
adress
Cette méthode reprend la même logique que la précédente pour la création de l'adresse pour une personne.
def
getText
(
self, node):
return
node.childNodes[0
].nodeValue
Et enfin celle-ci retourne le texte pour un nœud particulier. Ainsi pour le nœud « nom », je vais récupérer le texte, mais comme le texte est également un nœud, je suis obligé de faire « node.childNode[0] ». Le « nodeValue » est pour récupérer la valeur.
IV. Conclusion▲
Comme vous pouvez le voir, il est assez simple de manipuler un fichier XML avec python et l'API DOM. Pour les Java-istes, ça ressemble assez à du JDOM et donc on retrouve vite ses marques.