繁体   English   中英

验证未经验证的 XmlDocument 的特定元素

[英]Validate a specific element of an unvalidated XmlDocument

我有旧版 XML 文档,其中包含我想针对 XML 架构进行验证的嵌套(非根)元素。 模式本身并没有将 XML 文档作为一个整体来描述,而只是一个特定的嵌套元素。

XML 文档类似于从第三方系统收到的消息,没有xmlns属性,甚至没有 XML 处理指令。 这是我无法影响的遗产。 例子:

<XM>
    <MH> … nested header elements … </MH>
    <MD>
        <RECSET>
            … payload elements go here …
        </RECSET>
   </MD>
</XM>

我的目标是针对 XSD 验证/XM/MD/RECSET ,该 Z0CD3581B75ED89C665CFZ 定义了RECSET元素和嵌套在其中的任何有效负载元素。 我没有描述外部元素的模式,即XMMHMD 可以修改所有现有模式并添加虚拟定义,例如允许xs:all ,但这不是首选。

验证是处理管道中的一个可选步骤,我想避免不必要地重复 XML 解析和其他增加执行时间的处理(吞吐量很重要)。

另一个限制是我想使用XmlDocument ,因为在处理管道中我需要一个XmlDocument实例来使用XmlSerializer将反序列化到 object model 中。 同样,这是我想要保留的现有解决方案。

我的尝试如下:

// build an XmlDocument instance as the intermediate format of the message
var xml = new XmlDocument();
xml.LoadXml(msg.TransportMessage);

// obtain a pre-cached XmlSchemaSet instance matching the message represented by XmlDocument
XmlSchemaSet schemaSet = … ;

// find the whole payload represented by the RECSET element
var nodeToValidate = xml.SelectSingleNode("/XM/MD/RECSET");

// attach schemas to the document and validate the payload node
xml.Schemas = xsd;
xml.Validate(ValidationCallback, nodeToValidate);

这会导致错误:

找不到传递给验证的节点的架构信息。 该节点可能在其当前 position 中无效。 导航到具有架构信息的祖先,然后再次调用 Validate。

我研究了XmlDocumentDocumentSchemaValidator class的实现,在特定节点验证的情况下,它会在 DOM 中搜索架构信息。 因此,我尝试将对正确模式的引用附加到节点 ad hoc:

XmlAttribute noNamespaceAttribute =  xml.CreateAttribute("xsi:noNamespaceSchemaLocation", "http://www.w3.org/XMLSchema-instance");
foreach (XmlSchemaElement x in schemaSet.GlobalElements.Values)
{
    if (x.Name == "RECSET")
    {
        noNamespaceAttribute.InnerText = x.SourceUri!;
        break;
    }
}
nodeToValidate.Attributes!.Append(noNamespaceAttribute);

但是,这会导致完全相同的错误消息。

实现这种验证的一种工作方法是获取nodeToValidate.OuterXml并使用验证XmlReader或新的XmlDocument实例对其进行解析。 但是,这会导致 memory 和 CPU 方面的另一个开销。 我宁愿避开这条路线。

有没有办法告诉验证引擎根据明确指定的模式验证特定节点?

您的问题XmlDocument.Schemas旨在表示整个文档的架构:

当执行 XmlDocument 的 Validate 方法时,与 XmlDocument object 关联的 XmlSchemaSet object 中包含的架构用于验证。

在您的情况下,您没有整个文档的架构,因此当您尝试通过将XmlDocument.Schemas设置为该子节点的架构来验证文档的特定节点时,验证失败,可能是因为验证代码无法导航通过根文档的架构(不存在)来查找要检查的子元素的特定子架构。

解决方法的选项取决于您在调用XmlDocument.Validate(ValidationEventHandler, XmlNode)时要完成的工作。 文档中所述,此方法实际上执行两个不同但相关的操作:

  1. 正如预期的那样,它根据 Schemas 属性中包含的模式验证 XmlNode object 中的 XML 数据。

  2. 它还执行信息集扩充

    Validate方法执行信息集扩充。 具体来说,在成功验证后,应用架构默认值,必要时将文本值转换为原子值,并将类型信息与经过验证的信息项相关联。 结果是XmlDocument中先前未键入的 XML 子树替换为已键入的子树。

行动 #1 似乎很清楚,但究竟什么是信息集扩充 这没有明确记录,但一种效果是填充XmlNode.SchemaInfo的内容。 For instance, using the XML and XSD from https://www.w3schools.com/xml/schema_example.asp as an example, if I validate the root element against the XSD and check the contents of DocumentElement.SchemaInfo before and after as follows :

var nodeToValidate = xml.DocumentElement;

Console.WriteLine("DocumentElement.SchemaInfo before: {0}", new { nodeToValidate?.SchemaInfo.IsDefault,  nodeToValidate?.SchemaInfo.IsNil, nodeToValidate?.SchemaInfo.MemberType, nodeToValidate?.SchemaInfo.SchemaAttribute, nodeToValidate?.SchemaInfo.SchemaElement, nodeToValidate?.SchemaInfo.SchemaType, nodeToValidate?.SchemaInfo.Validity });

// attach schemas to the document and validate the payload node
xml.Schemas = schemaSet;
xml.Validate(ValidationCallback, nodeToValidate);

Console.WriteLine("DocumentElement.SchemaInfo after:  {0}", new { nodeToValidate?.SchemaInfo.IsDefault,  nodeToValidate?.SchemaInfo.IsNil, nodeToValidate?.SchemaInfo.MemberType, nodeToValidate?.SchemaInfo.SchemaAttribute, nodeToValidate?.SchemaInfo.SchemaElement, nodeToValidate?.SchemaInfo.SchemaType, nodeToValidate?.SchemaInfo.Validity });

结果清楚地表明DocumentElement.SchemaInfo已被填充。

DocumentElement.SchemaInfo before: { IsDefault = False, IsNil = False, MemberType = , SchemaAttribute = , SchemaElement = , SchemaType = , Validity = NotKnown }
DocumentElement.SchemaInfo after:  { IsDefault = False, IsNil = False, MemberType = , SchemaAttribute = , SchemaElement = System.Xml.Schema.XmlSchemaElement, SchemaType = System.Xml.Schema.XmlSchemaComplexType, Validity = Valid }

演示小提琴#1在这里

此外,似乎XmlDocument.Validate(ValidationEventHandler, XmlNode)实际上可能会将其他节点插入到文档中,请参阅XmlDocument.NodeInserted 在 XmlDocument.Validate() 上触发的一个这样的示例。

但是您真的需要通过信息集扩充来修改您的XmlDocument ,还是只需要执行只读验证?

如果您不需要信息集扩充,您可以通过从中构造XmlNodeReader来验证XmlNode ,然后使用阅读器进行只读验证。 首先介绍以下扩展方法:

public static class XmlNodeExtensions
{
    public static void Validate(this XmlNode node, XmlReaderSettings settings)
    {
        if (node == null)
            throw new ArgumentNullException(nameof(node));
        using (var innerReader = new XmlNodeReader(node))
        using (var reader = XmlReader.Create(innerReader, settings))
        {
            while (reader.Read())
                ;
        }
    }

    public static void Validate(this XmlNode node, XmlSchemaSet schemaSet, XmlSchemaValidationFlags validationFlags, ValidationEventHandler validationEventHandler)
    {
        if (node == null)
            throw new ArgumentNullException(nameof(node));
        var settings = new XmlReaderSettings();
        settings.ValidationType = ValidationType.Schema;
        settings.ValidationFlags |= validationFlags;
        if (validationEventHandler != null)
            settings.ValidationEventHandler += validationEventHandler;
        settings.Schemas = schemaSet;
        node.Validate(settings);
    }
}

现在您将能够:

XmlSchemaSet schemaSet = ...

var nodeToValidate = xml.SelectSingleNode("/XM/MD/RECSET");

nodeToValidate.Validate(schemaSet, default, ValidationCallback);

请注意,您永远不会使用这种方法设置XmlDocument.Schemas 演示小提琴#2在这里

如果您确实需要扩充信息集,您将需要重新考虑您的方法,可能通过在运行时以编程方式为<XM><MD>...</MD></XM>包装器元素生成一个合理的XmlSchema并将其添加到XmlDocument.Schemas验证前的XmlDocument.Schemas

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM