Überprüfen eines XML gegen referenziertes XSD in C #

161

Ich habe eine XML-Datei mit einem angegebenen Schema-Speicherort wie dem folgenden:

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

Ich möchte in C # validieren. Wenn ich die Datei in Visual Studio öffne, wird sie anhand des Schemas überprüft und die Fehler werden perfekt aufgelistet. Irgendwie kann ich es jedoch nicht automatisch in C # validieren, ohne das Schema anzugeben, gegen das validiert werden soll:

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);

Sollte ich nicht in der Lage sein, automatisch mit dem in der XML-Datei angegebenen Schema zu validieren? Was vermisse ich ?

jfclavette
quelle
1
Siehe das MSDN-Beispiel: msdn.microsoft.com/en-us/library/…

Antworten:

167

Sie müssen eine XmlReaderSettings-Instanz erstellen und diese beim Erstellen an Ihren XmlReader übergeben. Anschließend können Sie die ValidationEventHandlerin den Einstellungen abonnieren , um Validierungsfehler zu erhalten. Ihr Code sieht am Ende folgendermaßen aus:

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
quelle
4
+1 obwohl sollte aktualisiert werden, um die usingKlausel der Vollständigkeit
halber
55
Wenn Sie mit einer XSD-Datei vergleichen möchten, fügen Sie dem obigen Code die folgende Zeile hinzu: settings.Schemas.Add ("YourDomainHere", "yourXSDFile.xsd");
Jeff Fol
5
Um die Zeilen- und Positionsnummer des Fehlers abzurufen, verwenden Sie einfach: args.Exception.LineNumber ... in ValidationCallBack
user610064
1
Was ist, wenn das Schema, das ich habe, keinen Namespace hat?
Baum
1
Mit Lambda , besser IMHO, mehr Klarheit Code settings.ValidationEventHandler += (o, args) => { errors = true; // More code };
Kiquenet
106

Eine einfachere Möglichkeit, wenn Sie .NET 3.5 verwenden, ist die Verwendung XDocumentund XmlSchemaSetValidierung.

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);

Weitere Unterstützung finden Sie in der MSDN-Dokumentation .

saluce
quelle
2
Für diese Methode müssen Sie das Schema im Voraus kennen, anstatt das Inline-Schema aus der XML-Datei zu übernehmen.
Lankymart
Dies funktioniert einwandfrei, löst jedoch einen Fehler aus, wenn ein XML-Dokument ein HTML-Tag wie <Katalog> meines <i> neuen </ i> Katalogs enthält .... </ Katalog> in dem obigen Fall erstellen HTML-Tags wie "<i>" ein Problem als es ist der Wert von "<Katalog>" ... wie man es validiert
Anil Purswani
6
@AnilPurswani: Wenn Sie HTML in ein XML-Dokument einfügen möchten, müssen Sie es in CDATA einbinden. <catalog><![CDATA[my <i> new </i> catalog....]]></catalog>ist der richtige Weg, das zu tun.
p0lar_bear
Einfach und elegant! Dies funktioniert sehr gut bei der Validierung anhand eines festen Schemasatzes (was in unserem Fall der Fall ist, und eines großen mit mehreren Ordnern und Dateien). Ich denke bereits darüber nach, das XmlSchemaSet zwischenzuspeichern, um es zwischen Aufrufen des Validators wiederzuverwenden. Vielen Dank!
Adail Retamal
20

Das folgende Beispiel validiert eine XML-Datei und generiert den entsprechenden Fehler oder die entsprechende Warnung.

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);

    }
}

Im vorhergehenden Beispiel werden die folgenden Eingabedateien verwendet.

<?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
quelle
18

persönlich bevorzuge ich die Validierung ohne Rückruf:

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;
}

(Siehe Timiz0rs Beitrag in Synchronous XML Schema Validation? .NET 3.5 )

FrankyHollywood
quelle
9
Der Rückruf liefert Ihnen einige zusätzliche Informationen darüber, welche Zeile in Ihrer XML nicht korrekt ist. Diese Methode ist sehr binär, entweder richtig oder falsch :)
FrankyHollywood
13

Ich hatte diese Art der automatischen Validierung in VB durchgeführt und so habe ich es gemacht (konvertiert in C #):

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

Dann habe ich das settings.ValidationEventHandlerEreignis beim Lesen der Datei abonniert .

Welbog
quelle