【问题标题】:Getting started with XSD validation with .NET开始使用 .NET 进行 XSD 验证
【发布时间】:2010-03-25 04:18:25
【问题描述】:

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

要验证的 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>

XSD,位于“Schemas/config.xsd”中,相对于要验证的 XML 文件:

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

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

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

运行上面的代码会出现这个异常:

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

任何照明都将不胜感激。

【问题讨论】:

    标签: c# xml xsd xml-validation


    【解决方案1】:

    我不会使用XDocument.Validate 扩展方法,而是使用XmlReader,它可以配置为通过XmlReaderSettings 处理内联模式。您可以执行以下代码之类的操作。

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

    上面的代码假定以下 using 语句。

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

    为了简单起见,我没有返回 boolean 或验证错误集合,您可以轻松地修改它来这样做。

    注意:我修改了您的 config.xml 和 config.xsd 以让它们进行验证。这些是我所做的更改。

    config.xsd:

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

    config.xml:

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

    【讨论】:

    • 第一个答案对我有用,只是这里缺少错误处理://解析文件将导致验证发生。 while (reader.Read()) ;一些错误(比如有一个 XML 元素的开头,但没有别的)没有被捕获。更糟糕的是,它们甚至不会在调用代码中引发异常。
    • @user1264809 缺少的结束标记在此处作为 XmlException 抛出。如果您想在解析递归 xsd 包含时捕获错误,则必须包装 XmlSecureResolver 或 XmlUrlResolver 并计算该包装器中的异常,然后查询该数字。
    【解决方案2】:

    以下是工作示例:

    用法:

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

    类:

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

    上面的代码验证了 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>
    

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

    您也可以尝试相反的过程;尝试从您的 xml 生成架构并与您的实际 xsd 进行比较 - 查看差异;最简单的方法是使用 VS IDE 生成模式。以下是你的做法:

    希望这会有所帮助。

    --编辑--

    这是应 John 的要求,请查看使用非弃用方法的更新代码:

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

    用法:

    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(从 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>
    

    这保证有效!

    【讨论】:

    • -10!不使用块,不推荐使用“new XmlTextReader()”,不推荐使用“new XmlValidatingReader()”,XmlSchemaCollection 实际上已过时!你在使用 .NET 1.1???
    • @John:这是解决问题的方法;该操作没有为代码指定任何特定版本。不过,我已经满足了您的顾虑并添加了更新的代码。请查看编辑。
    • 如果有人没有指定版本,那么可以肯定地说他们不是指 .NET 1.1!您至少应该假设他们使用的是 .NET 2.0。我正在删除反对票。
    • 在您的使用代码中,xml 验证器称为 XMLValidator。在您的类 sn-p 中,该类本身称为 CXmlValidator。应该是同名。
    • 在 NET 4.0 中不推荐使用哪些方法
    【解决方案3】:

    您提取架构位置的代码看起来很奇怪。为什么要获取 xmlns 属性的值并将其与 xsi:noNamespaceSchemaLocation 属性的值连接起来?异常是由于您无法在对 Attributes 的调用中指定前缀;您需要指定所需的 XNamespace。

    试试这个(未经测试):

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

    【讨论】:

    • 我不知道 xmlns 是干什么用的。我什至需要它吗?我只想从要验证的文档中正确指向架构的位置。
    • 我认为您不需要 xmlns 属性的值。如上所示使用xsi + "noNamespaceSchemaLocation"
    • 为了记录,xml解析器使用xmlns知道xsi:noNamespaceSchemaLocation中的xsi指的是模式http://www.w3.org/2001/XMLSchema-instance。解析文档后,您使用XNamespace 来引用命名空间,而xmlns 属性不再相关。
    猜你喜欢
    • 2011-03-10
    • 2012-04-13
    • 2012-09-12
    • 2014-03-27
    • 2016-10-18
    • 1970-01-01
    • 1970-01-01
    • 2023-03-12
    相关资源
    最近更新 更多