使用XSD进行部分XML文件验证

时间:2016-05-03 13:12:04

标签: c# xml validation xsd

我正在尝试使用XDocument类和XmlSchemaSet类来验证XMl文件。

XML文件已经存在但我只想添加一个由其他元素组成的元素,我只想验证这个节点。

以下是XML文件的示例。我要验证的是TestConfiguration节点:

<?xml version="1.0" encoding="ISO-8859-1"?>
<Root>
  <AppType>Test App</AppType>
  <LabelMap>
    <Label0>
        <Title>Tests</Title>
        <Indexes>1,2,3</Indexes>
    </Label0>
  </LabelMap>

<TestConfiguration>
    <CalculateNumbers>true</CalculateNumbers>
    <RoundToDecimalPoint>3</RoundToDecimalPoint>
</TestConfiguration>
</Root>

到目前为止,这是我的xsd:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="TestConfiguration"
           targetNamespace="MyApp_ConfigurationFiles" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <xs:element name="TestConfiguration">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="CalculateNumbers" type="xs:boolean" minOccurs="1" maxOccurs="1"/>
        <xs:element name="RoundToDecimalPoint" type="xs:int" minOccurs="1" maxOccurs="1"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

以下是我用来验证它的代码:

private bool ValidateXML(string xmlFile, string xsdFile)
{
    string xsdFilePath = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) ?? string.Empty, xsdFile);

    Logger.Info("Validating XML file against XSD schema file.");
    Logger.Info("XML: " + xmlFile);
    Logger.Info("XSD: " + xsdFilePath);

    try
    {
        XDocument xsdDocument = XDocument.Load(xsdFilePath);
        XmlSchemaSet schemaSet = new XmlSchemaSet();
        schemaSet.Add(XmlSchema.Read(new StringReader(xsdDocument.ToString()), this.XmlValidationEventHandler));
        XDocument xmlDocument = XDocument.Load(xmlFile);
        xmlDocument.Validate(schemaSet, this.XmlValidationEventHandler);
    }
    catch (Exception e)
    {
        Logger.Info("Error parsing XML file: " + xmlFile);
        throw new Exception(e.Message);
    }

    Logger.Info("XML validated against XSD.");
    return true;
}

即使验证完整的XML文件,验证也会成功通过,当我尝试将XML文件加载到由xsd2code创建的生成的类文件中时,我会遇到问题,错误:<Root xmlns=''> was not expected.

如何仅验证TestConfiguration件?

由于

1 个答案:

答案 0 :(得分:1)

这里有一些问题:

  1. 验证整个文档失败时会成功。

    这是因为架构未知根节点,并且遇到未知节点被视为验证警告而不是验证错误 - 即使该未知节点是根元素。要在验证时启用警告,您需要设置XmlSchemaValidationFlags.ReportValidationWarnings。但是,没有办法将此标志传递给XDocument.Validate()。问题XDocument.Validate is always successful显示了解决此问题的一种方法。

    完成此操作后,您还必须在ValidationEventArgs.Severity == XmlSeverityType.Warning时在验证处理程序中抛出异常。

    (至于要求XSD中的某个根元素,这显然是not possible。)

  2. 您需要一种方便的方法来验证元素以及文档,这样您就可以验证<TestConfiguration>件。

  3. 您的XSD和XML不一致。

    您XSD指定您的元素位于行MyApp_ConfigurationFiles中的XML名称空间targetNamespace="MyApp_ConfigurationFiles" elementFormDefault="qualified"中。实际上,您问题中显示的XML元素不在任何名称空间中。

    如果XSD正确,则您的XML根节点需要如下所示:

    <Root xmlns="MyApp_ConfigurationFiles">
    

    如果XML正确,您的XSD需要如下所示:

    <xs:schema id="TestConfiguration"
       elementFormDefault="unqualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    
  4. 解决了#3中的XSD和XML不一致之后,您可以通过引入以下扩展方法来解决问题#1和#2,这些方法可以验证文档和元素:

    public static class XNodeExtensions
    {
        public static void Validate(this XContainer node, XmlReaderSettings settings)
        {
            if (node == null)
                throw new ArgumentNullException();
            using (var innerReader = node.CreateReader())
            using (var reader = XmlReader.Create(innerReader, settings))
            {
                while (reader.Read())
                    ;
            }
        }
    
        public static void Validate(this XContainer node, XmlSchemaSet schemaSet, XmlSchemaValidationFlags validationFlags, ValidationEventHandler validationEventHandler)
        {
            var settings = new XmlReaderSettings();
            settings.ValidationType = ValidationType.Schema;
            settings.ValidationFlags |= validationFlags;
            if (validationEventHandler != null)
                settings.ValidationEventHandler += validationEventHandler;
            settings.Schemas = schemaSet;
            node.Validate(settings);
        }
    }
    

    然后,要验证整个文档,请执行以下操作:

    try
    {
        var xsdDocument = XDocument.Load(xsdFilePath);
        var schemaSet = new XmlSchemaSet();
        using (var xsdReader = xsdDocument.CreateReader())
            schemaSet.Add(XmlSchema.Read(xsdReader, this.XmlSchemaEventHandler));
        var xmlDocument = XDocument.Load(xmlFile);
    
        xmlDocument.Validate(schemaSet, XmlSchemaValidationFlags.ReportValidationWarnings, XmlValidationEventHandler);
    }
    catch (Exception e)
    {
        Logger.Info("Error parsing XML file: " + xmlFile);
        throw new Exception(e.Message);
    }
    

    要验证特定节点,您可以使用相同的扩展方法:

    XNamespace elementNamespace = "MyApp_ConfigurationFiles";
    var elementName = elementNamespace + "TestConfiguration";
    
    try
    {
        var xsdDocument = XDocument.Load(xsdFilePath);
        var schemaSet = new XmlSchemaSet();
        using (var xsdReader = xsdDocument.CreateReader())
            schemaSet.Add(XmlSchema.Read(xsdReader, this.XmlSchemaEventHandler));
        var xmlDocument = XDocument.Load(xmlFile);
    
        var element = xmlDocument.Root.Element(elementName);
        element.Validate(schemaSet, XmlSchemaValidationFlags.ReportValidationWarnings, this.XmlValidationEventHandler);
    }
    catch (Exception e)
    {
        Logger.Info(string.Format("Error validating element {0} of XML file: {1}", elementName, xmlFile));
        throw new Exception(e.Message);
    }
    

    现在验证整个文档失败,同时使用以下验证事件处理程序验证{MyApp_ConfigurationFiles}TestConfiguration节点成功:

    void XmlSchemaEventHandler(object sender, ValidationEventArgs e)
    {
        if (e.Severity == XmlSeverityType.Error)
            throw new XmlException(e.Message);
        else if (e.Severity == XmlSeverityType.Warning)
            Logger.Info(e.Message);
    }
    
    void XmlValidationEventHandler(object sender, ValidationEventArgs e)
    {
        if (e.Severity == XmlSeverityType.Error)
            throw new XmlException(e.Message);
        else if (e.Severity == XmlSeverityType.Warning)
            throw new XmlException(e.Message);
    }