使用 XSD 验证部分 XML 文件 [英] Partial XML file validation using XSD

查看:29
本文介绍了使用 XSD 验证部分 XML 文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

这是 XML 文件的示例.我想验证的部分是 TestConfiguration 节点:

<根><AppType>测试应用</AppType><标签地图><标签0><Title>测试</Title><索引>1,2,3</索引></Label0></LabelMap><测试配置><CalculateNumbers>true</CalculateNumbers><RoundToDecimalPoint>3</RoundToDecimalPoint></TestConfiguration></Root>

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

<xs:element name="TestConfiguration"><xs:complexType><xs:序列><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("根据 XSD 架构文件验证 XML 文件.");Logger.Info("XML:" + xmlFile);Logger.Info("XSD:" + xsdFilePath);尝试{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);}捕获(例外 e){Logger.Info("解析 XML 文件时出错:" + xmlFile);抛出新的异常(e.Message);}Logger.Info("XML 针对 XSD 进行了验证.");返回真;}

即使验证完整的 XML 文件,验证也会成功通过,导致我在尝试将 XML 文件加载到 xsd2code 创建的生成的类文件中时遇到问题,错误:<Root xmlns=''>不是预期的..

如何仅验证 TestConfiguration 部分?

谢谢

解决方案

这里有几个问题:

  1. 当整个文档应该失败时验证它会成功.

    发生这种情况是因为模式未知根节点,遇到未知节点被视为验证警告而不是验证错误 - 即使该未知节点是根元素.要在验证时启用警告,您需要设置 XmlSchemaValidationFlags.ReportValidationWarnings.但是,无法将此标志传递给 <代码>XDocument.Validate().问题 XDocument.Validate 总是成功 显示了一种解决此问题的方法.>

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

    (至于在 XSD 中要求某个根元素,这显然是不可能的.)

  2. 您需要一种方便的方法来验证 元素 以及文档,以便验证您的 部分.

  3. 您的 XSD 和 XML 不一致.

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

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

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

解决 #3 中的 XSD 和 XML 不一致后,您可以通过引入以下验证文档和元素的扩展方法来解决问题 #1 和 #2:

公共静态类 XNodeExtensions{public static void Validate(这个XContainer节点,XmlReaderSettings设置){如果(节点==空)抛出新的 ArgumentNullException();使用 (var innerReader = node.CreateReader())使用 (var reader = XmlReader.Create(innerReader, settings)){而 (reader.Read());}}public static void Validate(这个XContainer节点,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;节点验证(设置);}}

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

试试{var xsdDocument = XDocument.Load(xsdFilePath);var schemaSet = new XmlSchemaSet();使用 (var xsdReader = xsdDocument.CreateReader())schemaSet.Add(XmlSchema.Read(xsdReader, this.XmlSchemaEventHandler));var xmlDocument = XDocument.Load(xmlFile);xmlDocument.Validate(schemaSet, XmlSchemaValidationFlags.ReportValidationWarnings, XmlValidationEventHandler);}捕获(例外 e){Logger.Info("解析 XML 文件时出错:" + xmlFile);抛出新的异常(e.Message);}

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

XNamespace elementNamespace = "MyApp_ConfigurationFiles";var elementName = elementNamespace + "TestConfiguration";尝试{var xsdDocument = XDocument.Load(xsdFilePath);var schemaSet = new XmlSchemaSet();使用 (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);}捕获(例外 e){Logger.Info(string.Format("Error validating element {0} of XML file: {1}", elementName, xmlFile));抛出新的异常(e.Message);}

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

void XmlSchemaEventHandler(object sender, ValidationEventArgs e){if (e.Severity == XmlSeverityType.Error)抛出新的 XmlException(e.Message);否则如果(e.Severity == XmlSeverityType.Warning)Logger.Info(e.Message);}void XmlValidationEventHandler(object sender, ValidationEventArgs e){if (e.Severity == XmlSeverityType.Error)抛出新的 XmlException(e.Message);否则如果(e.Severity == XmlSeverityType.Warning)抛出新的 XmlException(e.Message);}

I am trying to use the XDocument class and XmlSchemaSet class to validate an XMl file.

The XML file already exists but I want to add in just a single element consisting of a couple other elements and I only want to validate this node.

Here is an example of the XML file. The piece I would like to validate is the TestConfiguration node:

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

Here is my xsd so far:

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

Here is the code I use to validate it:

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

Even validating the full XML file, the validation will pass successfully causing me to run into problems when I try to load the XML file into the generated class file created by xsd2code, the error: <Root xmlns=''> was not expected..

How can I validate just the TestConfiguration piece?

Thanks

解决方案

You have a few issues here:

  1. Validating the entire document succeeds when it should fail.

    This happens because the root node is unknown to the schema, and encountering an unknown node is considered a validation warning not a validation error - even if that unknown node is the root element. To enable warnings while validating, you need to set XmlSchemaValidationFlags.ReportValidationWarnings. However, there's no way to pass this flag to XDocument.Validate(). The question XDocument.Validate is always successful shows one way to work around this.

    Having done this, you must also throw an exception in your validation handler when ValidationEventArgs.Severity == XmlSeverityType.Warning.

    (As for requiring a certain root element in your XSD, this is apparently not possible.)

  2. You need a convenient way to validate elements as well as documents, so you can validate your <TestConfiguration> piece.

  3. Your XSD and XML are inconsistent.

    You XSD specifies that your elements are in the XML namespace MyApp_ConfigurationFiles in the line targetNamespace="MyApp_ConfigurationFiles" elementFormDefault="qualified". In fact the XML elements shown in your question are not in any namespace.

    If the XSD is correct, your XML root node needs to look like:

    <Root xmlns="MyApp_ConfigurationFiles">
    

    If the XML is correct, your XSD needs to look like:

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

After you have resolved the XSD and XML inconsistency from #3, you can solve issues #1 and #2 by introducing the following extension methods that validate both documents and elements:

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

Then, to validate the entire document, do:

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

And to validate a specific node, you can use the same extension methods:

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

Now validating the entire document fails while validating the {MyApp_ConfigurationFiles}TestConfiguration node succeeds, using the following validation event handlers:

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

这篇关于使用 XSD 验证部分 XML 文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆