使用 XSD 部分 XML 文件验证

Partial XML file validation using XSD

我正在尝试使用 XDocument class 和 XmlSchemaSet class 来验证 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 文件加载到生成的 [=34= 中时遇到问题] 文件由 xsd2code 创建,错误:<Root xmlns=''> was not expected..

如何只验证 TestConfiguration 部分?

谢谢

你这里有几个问题:

  1. 验证整个文档在应该失败的时候成功了。

    发生这种情况是因为模式不知道根节点,遇到未知节点被认为是验证警告而不是验证错误 - 即使那个未知节点是根元素。要在验证时启用警告,您需要设置 XmlSchemaValidationFlags.ReportValidationWarnings. However, there's no way to pass this flag to XDocument.Validate(). The question XDocument.Validate is always successful 显示了一种解决此问题的方法。

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

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

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

  3. 你的XSD和XML不一致。

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

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

    <Root xmlns="MyApp_ConfigurationFiles">
    

    如果 XML 正确,您的 XSD 需要看起来像:

    <xs:schema id="TestConfiguration"
       elementFormDefault="unqualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    

在解决了#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);
}