簡體   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