Validation d'un XML par rapport à XSD référencé en C #

161

J'ai un fichier XML avec un emplacement de schéma spécifié tel que celui-ci:

xsi:schemaLocation="someurl ..\localSchemaPath.xsd"

Je veux valider en C #. Visual Studio, lorsque j'ouvre le fichier, le valide par rapport au schéma et répertorie parfaitement les erreurs. D'une certaine manière, cependant, je n'arrive pas à le valider automatiquement en C # sans spécifier le schéma à valider comme ceci:

XmlDocument asset = new XmlDocument();

XmlTextReader schemaReader = new XmlTextReader("relativeSchemaPath");
XmlSchema schema = XmlSchema.Read(schemaReader, SchemaValidationHandler);

asset.Schemas.Add(schema);

asset.Load(filename);
asset.Validate(DocumentValidationHandler);

Ne devrais-je pas pouvoir valider automatiquement avec le schéma spécifié dans le fichier XML? Qu'est-ce que je rate ?

jfclavette
la source
1
Reportez-vous à l'exemple MSDN: msdn.microsoft.com/en-us/library/…

Réponses:

167

Vous devez créer une instance XmlReaderSettings et la transmettre à votre XmlReader lorsque vous la créez. Ensuite, vous pouvez vous abonner à la ValidationEventHandlerdans les paramètres pour recevoir des erreurs de validation. Votre code finira par ressembler à ceci:

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

public class ValidXSD
{
    public static void Main()
    {

        // Set the validation settings.
        XmlReaderSettings settings = new XmlReaderSettings();
        settings.ValidationType = ValidationType.Schema;
        settings.ValidationFlags |= XmlSchemaValidationFlags.ProcessInlineSchema;
        settings.ValidationFlags |= XmlSchemaValidationFlags.ProcessSchemaLocation;
        settings.ValidationFlags |= XmlSchemaValidationFlags.ReportValidationWarnings;
        settings.ValidationEventHandler += new ValidationEventHandler(ValidationCallBack);

        // Create the XmlReader object.
        XmlReader reader = XmlReader.Create("inlineSchema.xml", settings);

        // Parse the file. 
        while (reader.Read()) ;

    }
    // Display any warnings or errors.
    private static void ValidationCallBack(object sender, ValidationEventArgs args)
    {
        if (args.Severity == XmlSeverityType.Warning)
            Console.WriteLine("\tWarning: Matching schema not found.  No validation occurred." + args.Message);
        else
            Console.WriteLine("\tValidation error: " + args.Message);

    }
}
Chris McMillan
la source
4
+1 mais devrait mettre à jour pour utiliser la usingclause d'exhaustivité :)
IAbstract
55
Si vous cherchez à comparer avec un fichier XSD, ajoutez la ligne suivante au code ci-dessus: settings.Schemas.Add ("YourDomainHere", "yourXSDFile.xsd");
Jeff Fol
5
Pour obtenir le numéro de ligne et la position # de l'erreur, utilisez simplement: args.Exception.LineNumber ... dans ValidationCallBack
user610064
1
Que faire si le schéma que j'ai n'a pas d'espace de noms?
arbre
1
Utilisation de lambda , meilleur IMHO, code plus de clartésettings.ValidationEventHandler += (o, args) => { errors = true; // More code };
Kiquenet
107

Un moyen plus simple, si vous utilisez .NET 3.5, consiste à utiliser XDocumentet à XmlSchemaSetvalider.

XmlSchemaSet schemas = new XmlSchemaSet();
schemas.Add(schemaNamespace, schemaFileName);

XDocument doc = XDocument.Load(filename);
string msg = "";
doc.Validate(schemas, (o, e) => {
    msg += e.Message + Environment.NewLine;
});
Console.WriteLine(msg == "" ? "Document is valid" : "Document invalid: " + msg);

Consultez la documentation MSDN pour plus d'assistance.

salue
la source
2
Cette méthode nécessite que vous connaissiez le schéma au préalable plutôt que de prendre le schéma en ligne du xml.
Lankymart
cela fonctionne bien mais génère une erreur lorsque le document xml contient une balise html comme <catalog> mon <i> nouveau </i> catalogue .... </catalog> dans le cas ci-dessus, les balises html comme "<i>" créent un problème car c'est la valeur de "<catalog>" ... comment le valider
Anil Purswani
6
@AnilPurswani: Si vous souhaitez mettre du HTML dans un document XML, vous devez l'envelopper dans CDATA. <catalog><![CDATA[my <i> new </i> catalog....]]></catalog>est la bonne façon de faire cela.
p0lar_bear
Simple et élégant! Cela fonctionne très bien lors de la validation par rapport à un ensemble de schémas fixe (ce qui est notre cas, et un grand avec plusieurs dossiers et fichiers). Je pense déjà à la mise en cache du XmlSchemaSet pour qu'il soit réutilisé entre les appels au validateur. Merci beaucoup!
Adail Retamal
20

L' exemple suivant valide un fichier XML et génère l'erreur ou l'avertissement approprié.

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

public class Sample
{

    public static void Main()
    {
        //Load the XmlSchemaSet.
        XmlSchemaSet schemaSet = new XmlSchemaSet();
        schemaSet.Add("urn:bookstore-schema", "books.xsd");

        //Validate the file using the schema stored in the schema set.
        //Any elements belonging to the namespace "urn:cd-schema" generate
        //a warning because there is no schema matching that namespace.
        Validate("store.xml", schemaSet);
        Console.ReadLine();
    }

    private static void Validate(String filename, XmlSchemaSet schemaSet)
    {
        Console.WriteLine();
        Console.WriteLine("\r\nValidating XML file {0}...", filename.ToString());

        XmlSchema compiledSchema = null;

        foreach (XmlSchema schema in schemaSet.Schemas())
        {
            compiledSchema = schema;
        }

        XmlReaderSettings settings = new XmlReaderSettings();
        settings.Schemas.Add(compiledSchema);
        settings.ValidationEventHandler += new ValidationEventHandler(ValidationCallBack);
        settings.ValidationType = ValidationType.Schema;

        //Create the schema validating reader.
        XmlReader vreader = XmlReader.Create(filename, settings);

        while (vreader.Read()) { }

        //Close the reader.
        vreader.Close();
    }

    //Display any warnings or errors.
    private static void ValidationCallBack(object sender, ValidationEventArgs args)
    {
        if (args.Severity == XmlSeverityType.Warning)
            Console.WriteLine("\tWarning: Matching schema not found.  No validation occurred." + args.Message);
        else
            Console.WriteLine("\tValidation error: " + args.Message);

    }
}

L'exemple précédent utilise les fichiers d'entrée suivants.

<?xml version='1.0'?>
<bookstore xmlns="urn:bookstore-schema" xmlns:cd="urn:cd-schema">
  <book genre="novel">
    <title>The Confidence Man</title>
    <price>11.99</price>
  </book>
  <cd:cd>
    <title>Americana</title>
    <cd:artist>Offspring</cd:artist>
    <price>16.95</price>
  </cd:cd>
</bookstore>

books.xsd

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns="urn:bookstore-schema"
    elementFormDefault="qualified"
    targetNamespace="urn:bookstore-schema">

 <xsd:element name="bookstore" type="bookstoreType"/>

 <xsd:complexType name="bookstoreType">
  <xsd:sequence maxOccurs="unbounded">
   <xsd:element name="book"  type="bookType"/>
  </xsd:sequence>
 </xsd:complexType>

 <xsd:complexType name="bookType">
  <xsd:sequence>
   <xsd:element name="title" type="xsd:string"/>
   <xsd:element name="author" type="authorName"/>
   <xsd:element name="price"  type="xsd:decimal"/>
  </xsd:sequence>
  <xsd:attribute name="genre" type="xsd:string"/>
 </xsd:complexType>

 <xsd:complexType name="authorName">
  <xsd:sequence>
   <xsd:element name="first-name"  type="xsd:string"/>
   <xsd:element name="last-name" type="xsd:string"/>
  </xsd:sequence>
 </xsd:complexType>

</xsd:schema>
Soroush
la source
18

personnellement je privilégie la validation sans rappel:

public bool ValidateSchema(string xmlPath, string xsdPath)
{
    XmlDocument xml = new XmlDocument();
    xml.Load(xmlPath);

    xml.Schemas.Add(null, xsdPath);

    try
    {
        xml.Validate(null);
    }
    catch (XmlSchemaValidationException)
    {
        return false;
    }
    return true;
}

(voir l'article de Timiz0r dans Synchronous XML Schema Validation? .NET 3.5 )

FrankyHollywood
la source
9
Le rappel vous fournit des informations supplémentaires sur la ligne incorrecte de votre XML. Cette méthode est très binaire, bonne ou mauvaise :)
FrankyHollywood
13

J'avais fait ce genre de validation automatique en VB et voici comment je l'ai fait (converti en C #):

XmlReaderSettings settings = new XmlReaderSettings();
settings.ValidationType = ValidationType.Schema;
settings.ValidationFlags = settings.ValidationFlags |
                           Schema.XmlSchemaValidationFlags.ProcessSchemaLocation;
XmlReader XMLvalidator = XmlReader.Create(reader, settings);

Puis je me suis abonné à l' settings.ValidationEventHandlerévénement en lisant le fichier.

Welbog
la source