开始使用.NET进行XSD验证

时间:2023-01-20 17:15:09

Here is my first attempt at validating XML with XSD.

这里是我第一次尝试使用XSD验证XML。

The XML file to be validated:

待验证的XML文件:

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

The XSD, located in "Schemas/config.xsd" relative to the XML file to be validated:

XSD位于“模式/配置”中。与待验证的XML文件相关的xsd:

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

Right now, I just want to validate the XML file precisely as it appears currently. Once I understand this better, I'll expand more. Do I really need so many lines for something as simple as the XML file as it currently exists?

现在,我只想准确地验证当前出现的XML文件。一旦我更好地理解了这一点,我将扩展更多。像XML文件这样简单的东西,我真的需要这么多行吗?

The validation code in C#:

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

Running the above code gives this exception:

运行上述代码会产生以下异常:

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

Any illumination would be appreciated.

如有任何启发,我们将不胜感激。

3 个解决方案

#1


26  

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.

而不是使用XDocument。验证扩展方法,我将使用可以配置为通过XmlReaderSettings处理内联模式的XmlReader。您可以执行如下代码。

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.

注意:我修改了您的配置。xml和配置。用于使它们生效。这些是我所做的改变。

config.xsd:

config.xsd:

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

config.xml:

config . xml:

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

#2


14  

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

上面的代码对xml(code3)后面的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:

在验证xml/xsd时,我得到的错误与您的不同;我认为这可以帮助您从这里继续(添加/删除xml元素):

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

错误http://www.freeimagehosting.net/uploads/01a570ce8b.jpg

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:

你也可以尝试相反的过程;尝试从您的xml生成模式,并与实际的xsd比较——看看区别;最简单的方法是使用VS IDE生成模式。以下是你如何做到的:

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

从XML http://i44.tinypic.com/15yhto3.jpg创建XSD

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

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)

Test2。XSD(来自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!

这是可以保证工作的!

#3


2  

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.

提取模式位置的代码看起来很奇怪。为什么要获取xmlns属性的值并将其与xsi: nonamespaceseschemalocation属性的值连接起来?这个异常是由于您不能在对属性的调用中指定前缀而引起的;您需要指定所需的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);
                      });

#1


26  

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.

而不是使用XDocument。验证扩展方法,我将使用可以配置为通过XmlReaderSettings处理内联模式的XmlReader。您可以执行如下代码。

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.

注意:我修改了您的配置。xml和配置。用于使它们生效。这些是我所做的改变。

config.xsd:

config.xsd:

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

config.xml:

config . xml:

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

#2


14  

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

上面的代码对xml(code3)后面的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:

在验证xml/xsd时,我得到的错误与您的不同;我认为这可以帮助您从这里继续(添加/删除xml元素):

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

错误http://www.freeimagehosting.net/uploads/01a570ce8b.jpg

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:

你也可以尝试相反的过程;尝试从您的xml生成模式,并与实际的xsd比较——看看区别;最简单的方法是使用VS IDE生成模式。以下是你如何做到的:

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

从XML http://i44.tinypic.com/15yhto3.jpg创建XSD

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

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)

Test2。XSD(来自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!

这是可以保证工作的!

#3


2  

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.

提取模式位置的代码看起来很奇怪。为什么要获取xmlns属性的值并将其与xsi: nonamespaceseschemalocation属性的值连接起来?这个异常是由于您不能在对属性的调用中指定前缀而引起的;您需要指定所需的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);
                      });