Utilisation des fichiers XSD en 5 min

Les fichiers XML Schema Definition (XSD) permettent de décrire la structure d’un document XML. Le grand intérêt de ce fichier est de servir à la validation du document XML en définisant des règles.

Génération automatique d’un fichier XSD à partir d’un fichier XML

Avec Xsd.exe

Cet outil fait partie du SDK Visual Studio. Il est accessible en utilisant une ligne de commandes dans le répertoire du SDK, par exemple:

C:\Program Files\Microsoft Visual Studio 9.0\SDK\v2.0\Bin

Pour générer le fichier XSD, il suffit d’écrire:

xsd [chemin du fichier XML] /outputdir:[répertoire où générer le fichier XSD]

Par exemple:

xsd fichier.xml /outputdir:"C:\MonRepertoire"

Par programmation

Le code suivant permet de générer un fichier XSD par programmation:

using System.Xml;
using System.Xml.Schema;
...

public void WriteXsd(string xmlFilePath)
{
    XmlReader reader = XmlReader.Create(xmlFilePath);
    XmlSchemaInference schema = new XmlSchemaInference();
    XmlSchemaSet schemaSet = schema.InferSchema(reader);
    
    foreach (XmlSchema s in schemaSet.Schemas())
    {
        using (var stringWriter = new StringWriter())
        {
            using (var writer = XmlWriter.Create(stringWriter))
            {
                s.Write(writer);
            }
    
            textbox.text = stringWriter.ToString();
        }
    }
}

Validation d’un fichier XML

Par programmation

Avec XmlSchemaSet

L’avantage de cette méthode est d’être utilisable à partir du Framework 2.0.
Un fichier XML peut être lu et validé de cette façon:

using System;
using System.Xml;
using System.Xml.Schema;

...

public void ValidateXmlFile(string schemaNamespace, string xsdFilePath, string xmlFilePath)
{
  XmlReaderSettings settings = new XmlReaderSettings();
  settings.Schemas.Add(schemaNamespace, xsdFilePath);
  settings.ValidationType = ValidationType.Schema;
  settings.ValidationEventHandler += new ValidationEventHandler(
     validationCallBack);
  XmlReader readItems = XmlReader.Create(xmlFilePath, settings);
  while (readItems.Read()) { }
}

private void validationCallBack(object sender, ValidationEventArgs e)
{
  if (e.Severity.Equals(XmlSeverityType.Warning))
  {
    Console.Write("WARNING: ");
    Console.WriteLine(e.Message);
  }
  else if (e.Severity.Equals(XmlSeverityType.Error))
  {
    Console.Write("ERROR: ");
    Console.WriteLine(e.Message);
  }
}

Ce code permet de valider un fichier XML suivant le schéma défini dans un fichier XSD. La validation s’effectue dans le namespace indiqué par le paramètre “schemaNamespace”.
Plus de détails sur MSDN.

Il est possible d’indiquer des critères suivant lesquels la validation sera effectuée.
Par exemple:

XmlReaderSettings settings = new XmlReaderSettings();
settings.ValidationType = ValidationType.Schema;
settings.ValidationFlags |= XmlSchemaValidationFlags.ProcessInlineSchema;
settings.ValidationFlags |= XmlSchemaValidationFlags.ReportValidationWarnings;
settings.ValidationEventHandler += new ValidationEventHandler (ValidationCallBack);

XmlSchemaValidationFlags.ProcessInlineSchema indique la validation traite des schémas inline trouvés. Les schémas inline étant des définitions inclues directement à l’intérieur de l’instance du fichier XML.
XmlSchemaValidationFlags.ReportValidationWarnings permet de signaler les avertissements de validation.
D’autres critères sont possibles pour l’enum XmlSchemaValidationFlags.

Plus de détails à propos de XmlReaderSettings.ValidationFlags sur MSDN.

Avec XDocument (LinQ to XML)

Cette méthode est possible à partir du Framework 3.5:

using System;
using System.Xml;
using System.Xml.Linq;

...

public void ValidateXmlFile(string schemaNamespace, string xsdFilePath, string xmlFilePath)
{
  XmlSchemaSet schemas = new XmlSchemaSet();
  schemas.Add(schemaNamespace, xsdFilePath);
  
  XDocument doc = XDocument.Load(xmlFilePath);
  string validationFeedbackMessage = string.Empty;
  doc.Validate(schemas, (objectSender, validationEventArgs) => {
      validationFeedbackMessage += validationEventArgs.Message + Environment.NewLine;
  });
  
  Console.WriteLine(msg == "" ? "Document is valid" : "Document invalid: " + validationFeedbackMessage);
}

Plus de détails à propos de ce type de validation sur MSDN.

A la compilation dans Visual Studio

Il est possible d’effectuer la validation d’un fichier XML à la compilation dans Visual Studio. Cette validation peut être pratique, par exemple, après avoir édité un fichier de configuration.
La validation s’effectue aussi lorsqu’on édite le fichier.

Cette fonctionnalité est disponible à partir de Visual Studio 2008. Pour l’utiliser, il faut:
1. Ajouter le fichier XML au projet
2. Ajouter le fichier XSD au projet
3. Faire un clique droit sur le fichier XML puis "Properties".
4. Cliquer sur les "…" du paramètre "Schemas".
5. Cliquer dans la colonne "Use" pour les lignes correspondant aux fichiers XSD à utiliser.

Attention aux "namespaces"

Voir plus bas pour plus de détails

Explication sur les "namespaces"

Les fichiers de schéma XSD utilisent des espaces de noms (i.e. "namespaces") pour distinguer les éléments appartenant au langage XSD et les éléments et attributs définis pour un schéma donné.
Dans l’en-tête d’un fichier XSD, on précise:
– le namespace des éléments appartenant au langage XSD
– si besoin, le namespace des éléments et attributs que l’on s’apprête à définir.

Namespace des éléments appartenant au langage XSD

On définit le namespace des éléments appartenant au langage XSD, on indique dans le fichier XSD:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  ...
</xs:schema>

xmlns signifie "xml namespace". xs est le préfixe utilisé pour éléments XSD.
Parfois on peut utiliser le préfixe xsd, dans ce cas le schéma sera défini par:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  ...
</xsd:schema>

Définir le namespace des éléments du schéma

On peut indiquer un namespace pour les éléments que l’on va définir dans le fichier XSD. Cette indication est facultative mais elle permet d’éviter les confusions. Ainsi on rajoute les attributs:
targetNamespace="http://yourdomain.org/namespace/" xmlns:this="http://yourdomain.org/namespace/".
targetNamespace est le namespace et xmlns:this précise le préfixe utilisé (qui sera “this”).
Si on définit le fichier XSD suivant:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://yourdomain.com/namespace/"
    xmlns:this="http://yourdomain.com/namespace/">
    ...
</xs:schema>

Alors pour faire référence à des éléments définis, il faut utiliser le préfixe "this". Par exemple, si on définit l’élément "Row" de cette façon:

<xs:complexType name="Row">
    <xs:sequence>
        <xs:element name="Name" type="xs:string" minOccurs="1" maxOccurs="1" />
        <xs:element name="Value" type="xs:float" minOccurs="1" maxOccurs="1" />
    </xs:sequence>
</xs:complexType>

Pour faire référence à cet élément dans le reste du fichier XSD, il faut utiliser le préfixe:

<xs:element name="Rows" type="this:Row" minOccurs="0" maxOccurs="unbounded" />

Ce préfixe est utilisé au niveau du fichier XSD. On n’est pas obligé d’utiliser le même préfixe dans le fichier XML car il est redéfinit dans l’en-tête du fichier XML.

L’utilisation d’un préfixe n’est pas obligatoire

Par exemple, dans le cas précédent si on supprime le préfixe, le fichier XSD sera:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://yourdomain.com/namespace/"
    xmlns="http://yourdomain.com/namespace/">
    ...
</xs:schema>

elementFormDefault

On utilise cet attribut pour indiquer si les éléments du namespace doivent être qualifiés ou non en utlisant le préfixe du namespace dans le fichier XML. Les 2 valeurs possibles sont: "qualified" et "unqualified". Par exemple, si on considère les éléments suivants définis dans un fichier XSD:

<xs:complexType name="AuthorType">
  <xs:sequence>
     <xs:element name="name" type="xs:string"/>
     <xs:element name="phone" type="xs:string"/>
  </xs:sequence>
</xs:complexType>
<xs:element name="author" type="this:AuthorType"/>

elementFormDefault="qualified"

Les éléments définis doivent être préfixés. Donc dans le fichier XML, on devra préfixer tous les éléments de l’instance (par forcément avec le même préfixe que dans le fichier XSD puisqu’on redéfinit le préfixe):

<x:author xmlns:x="http://example.org/publishing">
    <x:name>Aaron Skonnard</name>
    <x:phone>(801)390-4552</phone>
</x:author>

elementFormDefault="unqualified"

Il n’est pas nécessaire de préfixer tous les éléments de l’instance:

<x:author xmlns:x="http://example.org/publishing">
    <name>Aaron Skonnard</name>
    <phone>(801)390-4552</phone>
</x:author>

Généralement, on impose la présence du préfixe en utilisant la valeur "qualified" dans le fichier XSD:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://yourdomain.com/namespace/"
    xmlns:this="http://yourdomain.com/namespace/"
    elementFormDefault="qualified">
    ...
</xs:schema>

Association du fichier XML avec le fichier XSD

Un fichier XML dont les éléments sont définis dans un fichier XSD est considéré comme un document instance. Dans le fichier XML, on utilise l’attribut xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" pour désigner cette instance.
On utilise ensuite l’attribut xsi pour indiquer le fichier XSD définissant les éléments du fichier XML.

Si on n’utilise pas de namespaces

Rien n’oblige à utiliser des namespaces. Dans ce cas, dans le fichier XML on utilise l’attribut xsi:noNamespaceSchemaLocation.

Par exemple, si on considère le fichier XSD:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema 
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    elementFormDefault="qualified">
    <xs:element name="Rows" type="Row" minOccurs="0" maxOccurs="unbounded" />
    ...
</xs:schema>

Le fichier XML pourrait être:

<?xml version="1.0" encoding="UTF-8" ?>
<Rows 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="schemaFile.xsd">
    ...
</Rows>

Si on utilise des namespaces

Dans le fichier XML, en plus de l’attribut xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" qui permet de préciser l’instance, il faut utiliser:
xsi:schemaLocation pour indiquer le fichier XSD
– redéfinir le ou les namespaces utilisés avec, par exemple, un attribut: xmlns:this="http://yourdomain.com/namespace/" (on est pas obligé d’utiliser le même préfixe que pour le fichier XSD).

Par exemple si on définit le fichier XSD de la façon suivante:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema 
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://yourdomain.com/namespace/"
    xmlns:this="http://yourdomain.com/namespace/"
    elementFormDefault="qualified">
   ...
</xs:schema>

Le fichier XML correspondant pourrait être:

<?xml version="1.0" encoding="UTF-8" ?>
<Rows 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://yourdomain.com/namespace/ schemaFile.xsd"
    xmlns:this="http://yourdomain.com/namespace/">
    ...
</Rows>

Le préfixe "this" n’est pas obligatoire et n’est pas forcément le même que celui du fichier XSD.

Leave a Reply