2010-03-25 4 views
17

Voici ma première tentative de validation de XML avec XSD.Premiers pas avec la validation XSD avec .NET

Le fichier XML à valider:

<?xml version="1.0" encoding="utf-8" ?> 
<config xmlns="Schemas" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="config.xsd"> 
    <levelVariant> 
    <filePath>SampleVariant</filePath> 
    </levelVariant> 
    <levelVariant> 
    <filePath>LegendaryMode</filePath> 
    </levelVariant> 
    <levelVariant> 
    <filePath>AmazingMode</filePath> 
    </levelVariant> 
</config> 

Le XSD, situé dans "schemas/config.xsd" par rapport au fichier XML à valider:

<?xml version="1.0" encoding="utf-8" ?> 
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"> 
    <xs:element name="config"> 
    <xs:complexType> 
     <xs:sequence> 
     <xs:element name="levelVariant"> 
      <xs:complexType> 
      <xs:sequence> 
       <xs:element name="filePath" type="xs:anyURI"> 
       </xs:element> 
      </xs:sequence> 
      </xs:complexType> 
     </xs:element> 
     </xs:sequence> 
    </xs:complexType> 
    </xs:element> 
</xs:schema> 

En ce moment, je je veux juste valider le fichier XML précisément tel qu'il apparaît actuellement. Une fois que je comprendrai mieux, je développerai plus. Ai-je vraiment besoin de tant de lignes pour quelque chose d'aussi simple que le fichier XML tel qu'il existe actuellement?

Le code de validation en C#:

 public void SetURI(string uri) 
     { 
      XElement toValidate = XElement.Load(Path.Combine(PATH_TO_DATA_DIR, uri) + ".xml"); 

// begin confusion 

     // exception here 
     string schemaURI = toValidate.Attributes("xmlns").First().ToString() 
           + toValidate.Attributes("xsi:noNamespaceSchemaLocation").First().ToString(); 
     XmlSchemaSet schemas = new XmlSchemaSet(); 
     schemas.Add(null, schemaURI); 

     XDocument toValidateDoc = new XDocument(toValidate); 
     toValidateDoc.Validate(schemas, null); 
// end confusion 

      root = toValidate; 
     } 

Exécution du code ci-dessus donne cette exception:

The ':' character, hexadecimal value 0x3A, cannot be included in a name. 

Toute illumination serait appréciée.

Répondre

26

Plutôt que d'utiliser la méthode d'extension XDocument.Validate, j'utiliserais un XmlReader qui peut être configuré pour traiter un schéma en ligne via XmlReaderSettings. Vous pourriez faire quelque chose comme le code suivant.

public void VerifyXmlFile(string path) 
{ 
    // configure the xmlreader validation to use inline schema. 
    XmlReaderSettings config = new XmlReaderSettings(); 
    config.ValidationType = ValidationType.Schema; 
    config.ValidationFlags |= XmlSchemaValidationFlags.ReportValidationWarnings; 
    config.ValidationFlags |= XmlSchemaValidationFlags.ProcessInlineSchema; 
    config.ValidationFlags |= XmlSchemaValidationFlags.ProcessSchemaLocation; 
    config.ValidationEventHandler += new ValidationEventHandler(ValidationCallBack); 

    // Get the XmlReader object with the configured settings. 
    XmlReader reader = XmlReader.Create(path, config); 

    // Parsing the file will cause the validation to occur. 
    while (reader.Read()) ; 

} 

private void ValidationCallBack(object sender, ValidationEventArgs vea) 
{ 
    if (vea.Severity == XmlSeverityType.Warning) 
     Console.WriteLine(
      "\tWarning: Matching schema not found. No validation occurred. {0}", 
      vea.Message); 
    else 
     Console.WriteLine("\tValidation error: {0}", vea.Message); 

} 

Le code ci-dessus suppose les instructions d'utilisation suivantes.

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

Juste pour garder ce simple, je ne suis pas retourné un boolean ou une collection d'erreurs de validation, vous pouvez facilement modifier ce faire.

Remarque: J'ai modifié vos fichiers config.xml et config.xsd pour les valider. Ce sont les changements que j'ai faits.

config.xsd:

<xs:element maxOccurs="unbounded" name="levelVariant"> 

config.xml:

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="config.xsd"> 
+0

La première réponse a fonctionné pour moi, sauf qu'il manquait la gestion des erreurs ici: // L'analyse du fichier entraînera la validation. tandis que (lecteur.Lis()) ; Certaines erreurs (comme le début d'un élément XML mais rien d'autre) ne sont pas interceptées. Pire, ils ne provoquent même pas une exception dans le code appelant. –

2

Votre code pour extraire l'emplacement du schéma semble étrange. Pourquoi obtenez-vous la valeur de l'attribut xmlns et concattez-le avec la valeur de l'attribut xsi: noNamespaceSchemaLocation? L'exception est provoquée par le fait que vous ne pouvez pas spécifier un préfixe dans un appel aux attributs; vous devez spécifier le XNamespace désiré.

Essayez ceci (non testé):

// Load document 
XDocument doc = XDocument.Load("file.xml"); 

// Extract value of xsi:noNamespaceSchemaLocation 
XNamespace xsi = "http://www.w3.org/2001/XMLSchema-instance"; 
string schemaURI = (string)doc.Root.Attribute(xsi + "noNamespaceSchemaLocation"); 

// Create schema set 
XmlSchemaSet schemas = new XmlSchemaSet(); 
schemas.Add("Schemas", schemaURI); 

// Validate 
doc.Validate(schemas, (o, e) => 
         { 
          Console.WriteLine("{0}", e.Message); 
         }); 
+0

Je ne sais pas ce que xmlns est pour. Est-ce que j'en ai même besoin? Je veux juste pointer correctement vers l'emplacement du schéma du document à valider. –

+0

Je pense que vous ne devriez pas avoir besoin de la valeur de l'attribut xmlns. Utilisez 'xsi +" noNamespaceSchemaLocation "' comme indiqué ci-dessus. – dtb

+0

Pour l'enregistrement, xmlns est utilisé par le * xml parser * pour savoir que 'xsi' dans' xsi: noNamespaceSchemaLocation' fait référence au schéma 'http: // www.w3.org/2001/XMLSchema-instance'. Une fois le document analysé, vous utilisez 'XNamespace' pour faire référence aux espaces de noms et les attributs' xmlns' ne sont plus pertinents. – Zarat

14

Après est hors d'un échantillon de travail:

Utilisation:

XMLValidator val = new XMLValidator(); 
if (!val.IsValidXml(File.ReadAllText(@"d:\Test2.xml"), @"D:\Test2.xsd")) 
    MessageBox.Show(val.Errors); 

Classe:

public class CXmlValidator 
{ 
    private int nErrors = 0; 
    private string strErrorMsg = string.Empty; 
    public string Errors { get { return strErrorMsg; } } 
    public void ValidationHandler(object sender, ValidationEventArgs args) 
    { 
     nErrors++; 
     strErrorMsg = strErrorMsg + args.Message + "\r\n"; 
    } 

    public bool IsValidXml(string strXml/*xml in text*/, string strXsdLocation /*Xsd location*/) 
    { 
     bool bStatus = false; 
     try 
     { 
      // Declare local objects 
      XmlTextReader xtrReader = new XmlTextReader(strXsdLocation); 
      XmlSchemaCollection xcSchemaCollection = new XmlSchemaCollection(); 
      xcSchemaCollection.Add(null/*add your namespace string*/, xtrReader);//Add multiple schemas if you want. 

      XmlValidatingReader vrValidator = new XmlValidatingReader(strXml, XmlNodeType.Document, null); 
      vrValidator.Schemas.Add(xcSchemaCollection); 

      // Add validation event handler 
      vrValidator.ValidationType = ValidationType.Schema; 
      vrValidator.ValidationEventHandler += new ValidationEventHandler(ValidationHandler); 

      //Actual validation, read conforming the schema. 
      while (vrValidator.Read()) ; 

      vrValidator.Close();//Cleanup 

      //Exception if error. 
      if (nErrors > 0) { throw new Exception(strErrorMsg); } 
      else { bStatus = true; }//Success 
     } 
     catch (Exception error) { bStatus = false; } 

     return bStatus; 
    } 
} 

Le code ci-dessus valide xml (code3) en fonction de xsd (code4). En validant contre xml/xsd, je reçois des erreurs différentes de la vôtre; Je pense que cela peut vous aider à continuer (ajouter/supprimer des éléments XML) à partir d'ici:

Errors http://www.freeimagehosting.net/uploads/01a570ce8b.jpg

Vous pouvez également essayer le processus inverse; essayez de générer le schéma à partir de votre fichier XML et comparez avec votre xsd réel - voir la différence; et la façon la plus simple de le faire est d'utiliser le schéma généré avec l'IDE VS. Voici comment vous le faire:

Create XSD from XML http://i44.tinypic.com/15yhto3.jpg

Hope this helps.

--EDIT--

Ceci est à la demande de John, s'il vous plaît voir le code mis à jour en utilisant des méthodes non dépréciées:

public bool IsValidXmlEx(string strXmlLocation, string strXsdLocation) 
{ 
    bool bStatus = false; 
    try 
    { 
     // Declare local objects 
     XmlReaderSettings rs = new XmlReaderSettings(); 
     rs.ValidationType = ValidationType.Schema; 
     rs.ValidationFlags |= XmlSchemaValidationFlags.ProcessSchemaLocation | XmlSchemaValidationFlags.ReportValidationWarnings; 
     rs.ValidationEventHandler += new ValidationEventHandler(rs_ValidationEventHandler); 
     rs.Schemas.Add(null, XmlReader.Create(strXsdLocation)); 

     using (XmlReader xmlValidatingReader = XmlReader.Create(strXmlLocation, rs)) 
     { while (xmlValidatingReader.Read()) { } } 

     ////Exception if error. 
     if (nErrors > 0) { throw new Exception(strErrorMsg); } 
     else { bStatus = true; }//Success 
    } 
    catch (Exception error) { bStatus = false; } 

    return bStatus; 
} 

void rs_ValidationEventHandler(object sender, ValidationEventArgs e) 
{ 
    if (e.Severity == XmlSeverityType.Warning) strErrorMsg += "WARNING: " + Environment.NewLine; 
    else strErrorMsg += "ERROR: " + Environment.NewLine; 
    nErrors++; 
    strErrorMsg = strErrorMsg + e.Exception.Message + "\r\n"; 
} 

Utilisation:

if (!val.IsValidXmlEx(@"d:\Test2.xml", @"D:\Test2.xsd")) 
       MessageBox.Show(val.Errors); 
      else 
       MessageBox.Show("Success"); 

Test2 .XML

<?xml version="1.0" encoding="utf-8" ?> 
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="Test2.xsd"> 
    <levelVariant> 
    <filePath>SampleVariant</filePath> 
    </levelVariant> 
    <levelVariant> 
    <filePath>LegendaryMode</filePath> 
    </levelVariant> 
    <levelVariant> 
    <filePath>AmazingMode</filePath> 
    </levelVariant> 
</config> 

Test2.XSD (généré à partir de VS IDE)

<?xml version="1.0" encoding="utf-8" ?> 
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"> 
    <xs:element name="config"> 
    <xs:complexType> 
     <xs:sequence> 
     <xs:element maxOccurs="unbounded" name="levelVariant"> 
      <xs:complexType> 
      <xs:sequence> 
       <xs:element name="filePath" type="xs:anyURI"> 
       </xs:element> 
      </xs:sequence> 
      </xs:complexType> 
     </xs:element> 
     </xs:sequence> 
    </xs:complexType> 
    </xs:element> 
</xs:schema> 

Ceci est garanti pour travailler!

+5

-10! Pas d'utilisation de blocs, "new XmlTextReader()" est obsolète, "new XmlValidatingReader()" est obsolète, XmlSchemaCollection est en fait obsolète! Utilisez-vous .NET 1.1 ??? –

+0

@John: Ceci est une solution au problème; l'op n'a pas spécifié de version spécifique pour le code. Cependant, j'ai répondu à vos préoccupations et ajouté un code mis à jour. S'il vous plaît voir la modification. –

+1

Si quelqu'un ne spécifie pas une version, alors il est assez sûr de dire qu'ils ne veulent pas dire .NET 1.1! Vous devriez supposer qu'ils utilisent .NET 2.0 à tout le moins. Je supprime le downvote. –