Désérialisation de fichiers XML avec XSD.exe
Date de publication : 27/10/2007
Par
Ronald VASSEUR (autres articles)
Au cours de cet article nous allons voir comment désérialiser facilement un fichier XML dans
un objet grâce à VB.Net 2005 et un petit outil du SDK aussi pratique que méconnu. XSD.exe est
cet outil magique, à partir d'un schéma XSD il vous permettra de générer les centaines de
ligne de code mappant votre fichier XML avec un objet, au sens POO du terme. Nous aborderons aussi
le processus de sérialisation qui nous pemettra de passer de notre objet en mémoire à un fichier XML, la
boucle sera ainsi bouclée. Voici sans plus réaliser tout cela facilement avec .Net.
Introduction
1. Une petite démonstration en VB.Net
1.1. Fichier XML exemple
1.2. Génération du schéma XSD
1.3. Génération des classes
1.4. Désérialiser et sérialiser un fichier XML
Conclusion
Introduction
XML est plus que jamais le format à la mode. On le retrouve partout
et innombrables sont les applications à l'utiliser. En effet les
avantages d'XML sont nombreux, beaucoup plus pratique que les
fichiers textes ou binaires et bien moins lourd que les bases de
données, le XML est la solution idéale dans de nombreux cas. Pour
utiliser XML le Framework .Net propose de nombreuses classes et
méthodes qui permette de manipuler balises XML et leurs données, aussi bien en lecture qu'en écriture.
Bien que XMLDocument et les classes associées soient très pratiques
il est parfois fastidieux de manipuler les différents éléments des
fichiers XML. Une solution pour éviter de devoir manipuler un fichier
XML au niveau de sa structure et de ses balises consiste à employer
un niveau d'abstraction supplémentaire. Ainsi il devient possible
de manipuler des fichiers XML directement au niveau des données et de la logique
qu'elles comprennent. Ce petit tour de magie s'appuie sur un processus
connu et éprouvé depuis longtemps, la désérialisation. Pour ceux qui
l'ignorent la sérialisation (et déssérialisation) sont en fait un ensemble de processus
permettant d'assurer la persistance d'objets (au sens programmation
orientée objet) en traduisant par des données textes ou binaires un
objet stocké en mémoire. Concrètement, un objet donné se trouvant
en mémoire peut être sérialisé et ainsi stocké dans un fichier pour
assurer une persistance ou le transférer facilement sur un système
distant. Nous allons utiliser la sérialisation XML, c'est-à-dire
que les données représentant un objet qui a été sérialisé sont au format XML, un
autre type de sérialisation est la sérialisation binaire.

Pour être un peu plus clair, nous allons transformer nos fichiers
XML en objets chargés en mémoire, ils deviennent alors simples et pratiques
à utiliser. Passer d'un fichier XML à un objet en mémoire s'appelle
la désérialisation, alors que le processus inverse se nomme la
sérialisation. Potentiellement tout objet en mémoire peut être
sérialisé dans un fichier XML, dans la pratique il y a parfois
des limitations, de plus le code de (ou des) la classe(s) contenant cet objet
doit être "décoré" d'un certain nombre d'attributs ; mais cela n'est
pas le thème de cet article, et de plus vous allez voir nous allons
grandement nous simplifier la vie. Ici nous partons d'un fichier
XML, et nous souhaitons le désérialiser dans un objet, pour ce
faire nous avons besoin de classes mappant fidèlement tous les
champs contenus dans le fichier XML. En effet la représentation
mémoire des objets et de leurs données en programmation objet
passe par des classes, rien de bien révolutionnaire jusqu'ici ! De
plus toutes ces classes et champs vont devoir posséder un certain
nombre d'attributs pour permettre cette désérialisation/sérialisation par la CLR,
cela peut être écrit et codé manuellement, mais cela peut être très long
et fastidieux, je vous propose d'automatiser cela. Vous allez voir,
je n'ai rien inventé dans ce domaine, je vais me contenter de
réutiliser un outil fournit dans le SDK du Framework .Net, il se
nomme XSD.exe. Cet utilitaire en ligne de commande se propose de
réaliser principalement deux choses, générer un schéma XSD à partir
d'un fichier XML, et de générer des classes mappant un fichier XML
à partir du schéma XSD associé à ce fichier XML. J'ai décidé
d'écrire cet article car je me suis rendu compte que de nombreux
développeurs ne le connaissent pas et perde beaucoup de temps à
écrire des centaines de lignes de code qui pourraient être générées
en quelques dixièmes de seconde !
 |
Remarque : l'utilitaire XSD.exe est disponible dans le SDK du Framework
.Net 2.0, vous le trouverez dans ce répertoire "C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin"
|
1. Une petite démonstration en VB.Net
Assez parlé, passons à la démo. Nous allons ici partir d'un fichier
XML contenant des données, nous allons générer son schéma XSD,
c'est-à-dire une représentation standardisée de sa structure et de ses données, et
ensuite générer le code permettant de réaliser un objet dans lequel
désérialiser notre objet XML de départ.
1.1. Fichier XML exemple
Fichier XML exemple |
<? xml version="1.0" encoding="utf-8"? >
< Customers xmlns = " http://tempuri.org/customers.xsd " >
< Customer id = " 0 " >
< LastName title = " Mr " > Gates< / LastName >
< FirstName > Bill< / FirstName >
< Address >
< Street > One Microsoft Way< / Street >
< ZipCode > WA 98052< / ZipCode >
< City > Redmond< / City >
< / Address >
< / Customer >
< Customer id = " 1 " >
< LastName title = " Mr " > Ballmer< / LastName >
< FirstName > Steve< / FirstName >
< Address >
< Street > One Microsoft Way< / Street >
< ZipCode > WA 98052< / ZipCode >
< City > Redmond< / City >
< / Address >
< / Customer >
< / Customers >
|
1.2. Génération du schéma XSD
La première étape consiste donc à générer le fichier XSD contenant
le schéma de notre fichier XML, Visual Studio peut le faire
pour vous depuis le menu "XML" -> "Create Schema", ou
l'outil XSD.exe peut aussi le générer. Pour cela veuillez
utiliser la commande suivante :
C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin>xsd.exe C:\customers.xml /outputdir:C:\
Cette commande indique que nous devons générer un schéma XSD à partir
du fichier Customers.xml et que ce schéma doit être écrit à la
racine du disque C. Ce fichier sera nommé Customers.xsd.
Voici à quoi ressemble le schéma XSD :
Schéma XSD |
<? xml version="1.0" encoding="utf-8"? >
< xs : schema attributeFormDefault = " unqualified "
elementFormDefault = " qualified "
targetNamespace = " http://tempuri.org/customers.xsd "
xmlns : xs = " http://www.w3.org/2001/XMLSchema " >
< xs : element name = " Customers " >
< xs : complexType >
< xs : sequence >
< xs : element maxOccurs = " unbounded " name = " Customer " >
< xs : complexType >
< xs : sequence >
< xs : element name = " LastName " >
< xs : complexType >
< xs : simpleContent >
< xs : extension base = " xs:string " >
< xs : attribute name = " title " type = " xs:string " use = " required " / >
< / xs : extension >
< / xs : simpleContent >
< / xs : complexType >
< / xs : element >
< xs : element name = " FirstName " type = " xs:string " / >
< xs : element name = " Address " >
< xs : complexType >
< xs : sequence >
< xs : element name = " Street " type = " xs:string " / >
< xs : element name = " ZipCode " type = " xs:string " / >
< xs : element name = " City " type = " xs:string " / >
< / xs : sequence >
< / xs : complexType >
< / xs : element >
< / xs : sequence >
< xs : attribute name = " id " type = " xs:unsignedByte " use = " required " / >
< / xs : complexType >
< / xs : element >
< / xs : sequence >
< / xs : complexType >
< / xs : element >
< / xs : schema >
|
1.3. Génération des classes
Maintenant que nous avons le schéma XSD il ne nous reste plus
qu'à générer les classes permettant d'avoir un objet dans lequel
désérialiser notre fichier XML. Pour cela il faut utiliser
la commande suivante :
C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin>xsd.exe /l:vb /c C:\customers.xsd /outputdir:C:\
Cette commande va générer un fichier customers.vb à la racine du
disque C. Ce fichier contient le code VB.Net mappant le fichier
XML dans des classes. Cet outil permet de générer du code VB.Net
mais aussi C#, J# ou même JScript ; pour cela il suffit juste
de modifier le /l : dans la commande avec respectivement le
paramètre "vb", "cs", "vjs" ou "js". Magique n'est-ce pas !?
Voici le code VB.Net généré :
Classes mappant le fichier XML dans un objet |
Option Strict Off
Option Explicit On
Imports System. Xml . Serialization
< System. CodeDom . Compiler . GeneratedCodeAttribute (" xsd " , " 2.0.50727.42 " ), _
System. SerializableAttribute (), _
System. Diagnostics . DebuggerStepThroughAttribute (), _
System. ComponentModel . DesignerCategoryAttribute (" code " ), _
System. Xml . Serialization . XmlTypeAttribute (AnonymousType:= True , _
[Namespace ]:= " http://tempuri.org/customers.xsd " ), _
System. Xml . Serialization . XmlRootAttribute ([Namespace ]:= " http://tempuri.org/customers.xsd " , IsNullable:= False )> _
Partial Public Class Customers
Private itemsField () As CustomersCustomer
< System. Xml . Serialization . XmlElementAttribute (" Customer " )> _
Public Property Items () As CustomersCustomer ()
Get
Return Me. itemsField
End Get
Set (ByVal value As CustomersCustomer ())
Me. itemsField = value
End Set
End Property
End Class
< System. CodeDom . Compiler . GeneratedCodeAttribute (" xsd " , " 2.0.50727.42 " ), _
System. SerializableAttribute (), _
System. Diagnostics . DebuggerStepThroughAttribute (), _
System. ComponentModel . DesignerCategoryAttribute (" code " ), _
System. Xml . Serialization . XmlTypeAttribute (AnonymousType:= True , _
[Namespace ]:= " http://tempuri.org/customers.xsd " )> _
Partial Public Class CustomersCustomer
Private firstNameField As String
Private lastNameField () As CustomersCustomerLastName
Private addressField () As CustomersCustomerAddress
Private idField As String
Public Property FirstName () As String
Get
Return Me. firstNameField
End Get
Set (ByVal value As String )
Me. firstNameField = value
End Set
End Property
< System. Xml . Serialization . XmlElementAttribute (" LastName " , IsNullable:= True )> _
Public Property LastName () As CustomersCustomerLastName ()
Get
Return Me. lastNameField
End Get
Set (ByVal value As CustomersCustomerLastName ())
Me. lastNameField = value
End Set
End Property
< System. Xml . Serialization . XmlElementAttribute (" Address " )> _
Public Property Address () As CustomersCustomerAddress ()
Get
Return Me. addressField
End Get
Set (ByVal value As CustomersCustomerAddress ())
Me. addressField = value
End Set
End Property
< System. Xml . Serialization . XmlAttributeAttribute ()> _
Public Property id () As String
Get
Return Me. idField
End Get
Set (ByVal value As String )
Me. idField = value
End Set
End Property
End Class
< System. CodeDom . Compiler . GeneratedCodeAttribute (" xsd " , " 2.0.50727.42 " ), _
System. SerializableAttribute (), _
System. Diagnostics . DebuggerStepThroughAttribute (), _
System. ComponentModel . DesignerCategoryAttribute (" code " ), _
System. Xml . Serialization . XmlTypeAttribute (AnonymousType:= True , _
[Namespace ]:= " http://tempuri.org/customers.xsd " )> _
Partial Public Class CustomersCustomerLastName
Private titleField As String
Private valueField As String
< System. Xml . Serialization . XmlAttributeAttribute ()> _
Public Property title () As String
Get
Return Me. titleField
End Get
Set (ByVal value As String )
Me. titleField = value
End Set
End Property
< System. Xml . Serialization . XmlTextAttribute ()> _
Public Property Value () As String
Get
Return Me. valueField
End Get
Set (ByVal value As String )
Me. valueField = value
End Set
End Property
End Class
< System. CodeDom . Compiler . GeneratedCodeAttribute (" xsd " , " 2.0.50727.42 " ), _
System. SerializableAttribute (), _
System. Diagnostics . DebuggerStepThroughAttribute (), _
System. ComponentModel . DesignerCategoryAttribute (" code " ), _
System. Xml . Serialization . XmlTypeAttribute (AnonymousType:= True , _
[Namespace ]:= " http://tempuri.org/customers.xsd " )> _
Partial Public Class CustomersCustomerAddress
Private streetField As String
Private zipCodeField As String
Private cityField As String
Public Property Street () As String
Get
Return Me. streetField
End Get
Set (ByVal value As String )
Me. streetField = value
End Set
End Property
Public Property ZipCode () As String
Get
Return Me. zipCodeField
End Get
Set (ByVal value As String )
Me. zipCodeField = value
End Set
End Property
Public Property City () As String
Get
Return Me. cityField
End Get
Set (ByVal value As String )
Me. cityField = value
End Set
End Property
End Class
|
1.4. Désérialiser et sérialiser un fichier XML
Avec ce code vous pouvez désérialiser votre fichier XML dans
un objet. Pour désérialiser et sérialiser un objet il n'y à
rien de compliqué à réaliser, le Framework une fois de plus
vous simplifie la vie, voici une manière de procéder :
Désérialiser un fichier XML dans un objet |
Public Shared Sub Deserialize (ByVal filePath As String , ByRef obj As Object)
Dim deserializer As New XmlSerializer (obj. [GetType]())
Dim stream As Stream = New FileStream (filePath, FileMode. Open , FileAccess. Read , FileShare. None )
Try
obj = deserializer. Deserialize (stream)
Catch ex As Exception
Debug. WriteLine (" Error in Deserialisation " )
Throw ex
Finally
stream. Close ()
End Try
End Sub
|
Vous l'aurez compris, il ne reste plus qu'à sérialiser l'objet dans un
fichier XML pour assurer la persistance des données. Vous écrivez
simplement un fichier XML sur le disque dur, vos données
sont ainsi sauvegarder.
Sérialiser un objet dans un fichier XML |
Public Shared Sub Serialize (ByVal filePath As String , ByVal obj As Object)
Dim serializer As New XmlSerializer (obj. [GetType]())
Dim stream As Stream = New FileStream (filePath, FileMode. Create , FileAccess. Write , FileShare. None )
Try
serializer. Serialize (stream, obj)
Catch ex As Exception
Debug. WriteLine (" Error in serialisation " )
Throw ex
Finally
stream. Close ()
End Try
End Sub
|
J'espère que cette démonstration vous aura convaincu du fait
que cette technique en plus d'être simple et est très rapide
à mettre en œuvre grâce à cet utilitaire du SDK. De plus cette solution
couplée à la classe XmlSerializer du Framework vous offre une
productivité très elevée.
Conclusion
Vous l'avez vu, ce petit outil de 76Ko seuelment peut rendre votre vie de développeur plus
facile en générant pour vous, dans votre langage préféré, l'objet
permettant de manipuler les données d'un fichier directement dans
un objet sans avoir à gérer les structures parfois complexes des fichiers
XML. Un simple processus de désérialisation et de sérialisation vous
permettra de réaliser tout cela. La classe XMLSerializer est ici
simplissime à utiliser.
Attention cependant, aucune solution n'est parfaite. En terme de performance, la désérialisation
et sérialisation n'est pas une opération anodine, et sur de gros
fichiers cela s'avérer pénalisant. De plus cela peut ne pas convenir
à des applications qui doivent impacter dans des fichiers les
modifications réalisées. En clair, si vous devez écrire, ou même accéder au
fichier XML toutes les cinq secondes il vaudra mieux chercher une
autre solution.
Cet outil, même s'il n'est pas idéal permettra de couvrir de
nombreuses situations, et me semble une bonne solution pour éviter
d'avoir à écrire des centaines de lignes de code qui sont ma foi
bien ennuyeuses à écrire, et dans lesquelles il serait aisé de
glisser quelques petites erreurs. En attendant LINQ pour XML dans
la version 3.5 du Framework .Net prévu pour la fin de cette année
je pense que cela reste le meilleur moyen de travailler simplement
avec des fichiers XML qui peuvent être parfois complexes.


Copyright © 2007 Ronald Vasseur.
Aucune reproduction, même partielle, ne peut être faite
de ce site ni de l'ensemble de son contenu : textes, documents, images, etc.
sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à
trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.