Getting started with XSD validation with .NET

前端 未结 3 1642
鱼传尺愫
鱼传尺愫 2020-12-14 09:27

Here is my first attempt at validating XML with XSD.

The XML file to be validated:




        
相关标签:
3条回答
  • 2020-12-14 09:41

    Rather than using the XDocument.Validate extension method, I would use an XmlReader which can be configured to process an inline schema via XmlReaderSettings. You could do some thing like the following code.

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

    The code above assumes the following using statements.

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

    Just to keep this simple I did not return a boolean or a collection of validation errors, you could easily modify this to do so.

    Note: I modified your config.xml and config.xsd to get them to validate. These are the changes I made.

    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 讨论(0)
  • 2020-12-14 09:53

    Following is out of a working sample:

    Usage:

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

    Class:

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

    The above code validates following xml(code3) against xsd(code4).

    <!--CODE 3 - TEST1.XML-->
    <address xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="Test1.xsd"> 
    <name>My Name</name>
    <street>1, My Street Address</street>
    <city>Far</city>
    <country>Mali</country>
    </address>
    
    <!--CODE 4 - TEST1.XSD-->
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="address">
    <xs:complexType>
    <xs:sequence>
    <xs:element name="name" type="xs:string"/>
    <xs:element name="street" type="xs:string"/>
    <xs:element name="city" type="xs:string"/>
    <xs:element name="country" type="xs:string"/>
    </xs:sequence>
    </xs:complexType>
    </xs:element>
    </xs:schema>
    

    In validating against your xml/xsd I get of errors different than yours; I think this can help you continue(add/remove xml elements) from here:

    Errors

    You may also try the reverse process; try generating the schema from your xml and compare with your actual xsd - see the difference; and the easiest way to do that is to use generate schema using VS IDE. Following is how you'd do that:

    Hope this helps.

    --EDIT--

    This is upon John's request, please see updated code using non deprecated methods:

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

    Usage:

    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 (Generated from 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>
    

    This is guaranteed to work!

    0 讨论(0)
  • 2020-12-14 09:56

    Your code to extract the schema location looks weird. Why do you get the value of the xmlns attribute and concatenate it with the value of the xsi:noNamespaceSchemaLocation attribute? The exception is caused by the fact that you cannot specify a prefix in a call to Attributes; you need to specify the desired XNamespace.

    Try this (untested):

    // 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 讨论(0)
提交回复
热议问题