C# 如何获取XML模式验证失败位置的XPath(或节点)?
我正在使用(它的功能似乎与XmlDocument.Validate相同)根据XSD验证XML文档-这很有效,并且我知道验证错误 但是,在(和XmlSchemaException)中似乎只有一些信息[可靠地]公开,例如:C# 如何获取XML模式验证失败位置的XPath(或节点)?,c#,.net,linq-to-xml,xmldocument,xml-validation,C#,.net,Linq To Xml,Xmldocument,Xml Validation,我正在使用(它的功能似乎与XmlDocument.Validate相同)根据XSD验证XML文档-这很有效,并且我知道验证错误 但是,在(和XmlSchemaException)中似乎只有一些信息[可靠地]公开,例如: 错误消息(即“X”属性无效-值“Y”根据其数据类型“Z”无效-模式约束失败”) 严重性 我希望得到验证失败的“失败的XPath”(在有意义的地方):也就是说,我希望得到与XML文档相关的失败(与XML文本相反) 是否有方法从XDocument.Validate获取“失败的XP
- 错误消息(即“X”属性无效-值“Y”根据其数据类型“Z”无效-模式约束失败”)
- 严重性
XDocument.Validate
获取“失败的XPath”信息?如果不是,是否可以通过另一种XML验证方法(如XmlValidatingReader
1)获得“失败的XPath”
背景: XML将作为数据发送到我的Web服务,并进行JSON到XML的自动转换(通过JSON.NET)。因此,我开始处理XDocument数据1,而不是文本,因为原始JSON数据没有保证顺序。出于我不想深入讨论的原因,REST客户端基本上是XML文档上HTML表单字段的包装器,服务器上的验证分为两部分——XML模式验证和业务规则验证 在业务规则验证中,很容易为不符合要求的字段返回“XPath”,这些字段可用于指示客户端上的不符合要求的字段。我想将其扩展到XSD模式验证,它负责基本的结构验证,更重要的是,负责基本的“数据类型”和属性的“存在性”。但是,由于需要自动处理(即突出显示相应的失败字段)和源转换,原始文本消息和源行号/列号本身不是很有用
以下是验证代码的一个片段:
// Start with an XDocument object - created from JSON.NET conversion
XDocument doc = GetDocumentFromWebServiceRequest();
// Load XSD
var reader = new StringReader(EmbeddedResourceAccess.ReadResource(xsdName));
var xsd = XmlReader.Create(reader, new XmlReaderSettings());
var schemas = new XmlSchemaSet();
schemas.Add("", xsd);
// Validate
doc.Validate(schemas, (sender, args) => {
// Process validation (not parsing!) error,
// but how to get the "failing XPath"?
});
更新:我找到了指向()的链接,从中我确定了两件事:
XmlReader.read
)并“记住”验证回调之前的路径。虽然在最初的测试中(没有ValidationCallback),这个“看起来很有效”,但我觉得它很不雅观——但我几乎找不到其他东西我不知道API,但我的猜测是不,您无法获得xpath,因为验证可以作为有限状态机实现。一个状态可能不会转换为xpath,或者如果它对后面的多个元素有效,并且找到的元素不在预期的集合中,则xpath不存在。验证事件的发送方是事件的源。因此,您可以通过网络搜索获取节点XPath(例如)并生成事件源XPath的代码:
doc.Validate(schemas, (sender, args) => {
if (sender is XObject)
{
xpath = ((XObject)sender).GetXPath();
}
});
拿去吧:-)
var xpath=newstack();
var设置=新的XmlReaderSettings
{
ValidationType=ValidationType.Schema,
ValidationFlags=XmlSchemaValidationFlags.ReportValidationWarnings,
};
MyXmlValidationError错误=null;
settings.ValidationEventHandler+=(发送方,参数)=>error=ValidationCallback(发送方,参数);
foreach(模式中的var模式)
{
settings.Schemas.Add(schema);
}
使用(var reader=XmlReader.Create(xmlDocumentStream,设置))
{
//验证
while(reader.Read())
{
if(reader.NodeType==XmlNodeType.Element)
{
Push(reader.Name);
}
if(错误!=null)
{
//设置“失败的XPath”
error.XPath=XPath.Reverse().Aggregate(string.Empty,(x,y)=>x+“/”+y);
//您现在使用XPath时出错了
错误=null;
}
if(reader.NodeType==XmlNodeType.EndElement||
(reader.NodeType==XmlNodeType.Element&&reader.IsEmptyElement))
{
xpath.Pop();
}
}
}
或者,您可以使用处的代码通过使用args.Exception.LineNumber
和args.Exception.LinePosition
获取失败节点,然后根据需要导航XML文档,以提供有关导致验证失败的数据的更多信息。最终以这种方式成功
当我使用XmlReader.Create(xmlStream,settings)和xmlRdr.Read()来验证XML时,我捕获了ValidationEventHandler的发送者,发现它是{System.XML.XsdValidatingReader}的对象,所以我将发送者转移到一个XmlReader对象,XMLReader类中有一些函数可以帮助您找到错误属性的父节点
有一件事需要注意,当我使用XMLReader.MoveToElement()时,验证函数将卡在错误属性中的循环中,因此我使用MoveToAttribute(AttributeName),然后使用MoveToTextAttribute来避免卡在循环中,也许有更优雅的方法来处理这个问题
不用多说,下面是我的代码
public string XMLValidation(string XMLString, string SchemaPath)
{
string error = string.Empty;
MemoryStream xmlStream = new MemoryStream(Encoding.UTF8.GetBytes(XMLString));
XmlSchemaSet schemas = new XmlSchemaSet();
schemas.Add(null, SchemaPath);
XmlReaderSettings settings = new XmlReaderSettings();
settings.ValidationType = ValidationType.Schema;
settings.Schemas.Add(schemas);
settings.ValidationEventHandler += new ValidationEventHandler(delegate(object sender, ValidationEventArgs e)
{
switch (e.Severity)
{
case XmlSeverityType.Error:
XmlReader senRder = (XmlReader)sender;
if (senRder.NodeType == XmlNodeType.Attribute)
{//when error occurs in an attribute,get its parent element name
string attrName = senRder.Name;
senRder.MoveToElement();
error += string.Format("ERROR:ElementName'{0}':{1}{2}", senRder.Name, e.Message, Environment.NewLine);
senRder.MoveToAttribute(attrName);
}
else
{
error += string.Format("ERROR:ElementName'{0}':{1}{2}", senRder.Name, e.Message, Environment.NewLine);
}
break;
case XmlSeverityType.Warning:
break;
}
});
XmlReader xmlRdr = XmlReader.Create(xmlStream, settings);
while (xmlRdr.Read()) ;
return error;
}
如果像我一样,您正在使用“XmlDocument.Validate(ValidationEventHandler ValidationEventHandler)”方法验证您的XML:
// Errors and alerts collection
private ICollection<string> errors = new List<String>();
// Open xml and validate
...
{
// Create XMLFile for validation
XmlDocument XMLFile = new XmlDocument();
// Validate the XML file
XMLFile.Validate(ValidationCallBack);
}
// Manipulator of errors occurred during validation
private void ValidationCallBack(object sender, ValidationEventArgs args)
{
if (args.Severity == XmlSeverityType.Warning)
{
errors.Add("Alert: " + args.Message + " (Path: " + GetPath(args) + ")");
}
else if (args.Severity == XmlSeverityType.Error)
{
errors.Add("Error: " + args.Message + " (Path: " + GetPath(args) + ")");
}
}
请显示您正在使用的XML。。我们这里不是读心术的人。。thanks@DJKRAZE我不认为XML不重要——在我的例子中,我目前可以从10个XSD中选择一个,并针对这些XSD选择更多[故意]失败的测试用例数据。我不关心它失败的原因(这在信息中);我想得到失败的“XPath”——XML数据最终来自REST请求,然后
// Errors and alerts collection
private ICollection<string> errors = new List<String>();
// Open xml and validate
...
{
// Create XMLFile for validation
XmlDocument XMLFile = new XmlDocument();
// Validate the XML file
XMLFile.Validate(ValidationCallBack);
}
// Manipulator of errors occurred during validation
private void ValidationCallBack(object sender, ValidationEventArgs args)
{
if (args.Severity == XmlSeverityType.Warning)
{
errors.Add("Alert: " + args.Message + " (Path: " + GetPath(args) + ")");
}
else if (args.Severity == XmlSeverityType.Error)
{
errors.Add("Error: " + args.Message + " (Path: " + GetPath(args) + ")");
}
}
// Return this parent node
private string GetPath(ValidationEventArgs args)
{
var tagProblem =((XmlElement)((XmlSchemaValidationException)args.Exception).SourceObject);
return iterateParentNode(tagProblem.ParentNode) + "/" +tagProblem.Name;
}
private string iterateParentNode(XmlNode args)
{
var node = args.ParentNode;
if (args.ParentNode.NodeType == XmlNodeType.Element)
{
return interateParentNode(node) + @"/" + args.Name;
}
return "";
}