[英]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
元素和嵌套在其中的任何有效負載元素。 我沒有描述外部元素的模式,即XM
、 MH
、 MD
。 我可以修改所有現有模式並添加虛擬定義,例如允許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。
我研究了XmlDocument
和DocumentSchemaValidator
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)
時要完成的工作。 如文檔中所述,此方法實際上執行兩個不同但相關的操作:
正如預期的那樣,它根據 Schemas 屬性中包含的模式驗證 XmlNode object 中的 XML 數據。
它還執行信息集擴充:
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.