Developpez.com - Microsoft DotNET
X

Choisissez d'abord la catégorieensuite la rubrique :


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.

Microsoft .Net
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 !

info 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.

info Avec l'utilitaire XSD.exe tout se passe en ligne de commande, vous trouverez la documentation associée à cet outil dans le MSDN à cette adresse http://msdn2.microsoft.com/en-us/library/x6c1kb0s(vs.71).aspx

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

'------------------------------------------------------------------------------
' <auto-generated>
'     This code was generated by a tool.
'     Runtime Version:2.0.50727.832
'
'     Changes to this file may cause incorrect behavior and will be lost if
'     the code is regenerated.
' </auto-generated>
'------------------------------------------------------------------------------

Option Strict Off
Option Explicit On

Imports System.Xml.Serialization

'
'This source code was auto-generated by xsd, Version=2.0.50727.42.
'

'''<remarks/>
<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

    '''<remarks/>
    <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

'''<remarks/>
<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

    '''<remarks/>
    Public Property FirstName() As String
        Get
            Return Me.firstNameField
        End Get
        Set(ByVal value As String)
            Me.firstNameField = value
        End Set
    End Property

    '''<remarks/>
    <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

    '''<remarks/>
    <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

    '''<remarks/>
    <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

'''<remarks/>
<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

    '''<remarks/>
    <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

    '''<remarks/>
    <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

'''<remarks/>
<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

    '''<remarks/>
    Public Property Street() As String
        Get
            Return Me.streetField
        End Get
        Set(ByVal value As String)
            Me.streetField = value
        End Set
    End Property

    '''<remarks/>
    Public Property ZipCode() As String
        Get
            Return Me.zipCodeField
        End Get
        Set(ByVal value As String)
            Me.zipCodeField = value
        End Set
    End Property

    '''<remarks/>
    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.



Valid XHTML 1.1!Valid CSS!

Copyright © 2007 Ronald Vasseur. Aucune reproduction, même partielle, ne peut être faite de ce site et 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.

Responsable bénévole de la rubrique Microsoft DotNET : Hinault Romaric -