8

我正在使用XDocument.Validate(它的功能似乎与 XmlDocument.Validate 相同)针对 XSD 验证 XML 文档 - 这很好用并且我被告知验证错误。

但是,在ValidationEventHandler(和 XmlSchemaException)中似乎 [可靠地] 公开了一些信息,例如:

  • 错误消息(即“‘X’属性无效 - 根据其数据类型‘Z’,值‘Y’无效 - 模式约束失败”),
  • 严重性

想要的是获得验证失败的“失败的 XPath”(这是有意义的):也就是说,我想要获得与 XML 文档相关的失败(而不是 XML 文本)。

有没有办法从中获取“失败的 XPath”信息XDocument.Validate?如果不是,是否可以通过另一种 XML 验证方法(例如XmlValidatingReader1 )获得“失败的 XPath” ?


背景:

XML 将通过从 JSON 到 XML 的自动转换(通过 JSON.NET)作为数据发送到我的 Web 服务。因此,我开始处理 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"?
});

更新:我在验证 XDocument 时发现捕获架构信息,它链接到“在文档验证期间访问 XML 架构信息”缓存),我从中确定了两件事:

  1. XmlSchemaException 可以专门化为具有 SourceObject 属性的 XmlSchemaValidationException - 但是,这验证期间始终返回 null:“当验证 XmlReader 对象在验证期间抛出 XmlSchemaValidationException 时,SourceObject 属性的值为 null”。

  2. 我可以通读文档(通过XmlReader.Read)并在验证回调之前“记住”路径。虽然这在初始测试中“似乎有效”(没有 ValidationCallback),但对我来说感觉很不雅 - 但我几乎找不到其他东西。

4

6 回答 6

8

验证事件的发送者是事件的来源。因此,您可以在网络上搜索为节点获取 XPath 的代码(例如,生成 XPath 表达式)并为事件源生成 XPath:

doc.Validate(schemas, (sender, args) => {
  if (sender is XObject)
  { 
     xpath = ((XObject)sender).GetXPath();
  }
});
于 2013-02-04T06:20:25.523 回答
3

拿着 :-)

var xpath = new Stack<string>();

var settings = new XmlReaderSettings
               {
                   ValidationType = ValidationType.Schema,
                   ValidationFlags = XmlSchemaValidationFlags.ReportValidationWarnings,
               };
MyXmlValidationError error = null;
settings.ValidationEventHandler += (sender, args) => error = ValidationCallback(sender, args);
foreach (var schema in schemas)
{
    settings.Schemas.Add(schema);
}

using (var reader = XmlReader.Create(xmlDocumentStream, settings))
{
    // validation
    while (reader.Read())
    {
        if (reader.NodeType == XmlNodeType.Element)
        {
            xpath.Push(reader.Name);
        }

        if (error != null)
        {
            // set "failing XPath"
            error.XPath = xpath.Reverse().Aggregate(string.Empty, (x, y) => x + "/" + y);

            // your error with XPath now

            error = null;
        }

        if (reader.NodeType == XmlNodeType.EndElement ||
            (reader.NodeType == XmlNodeType.Element && reader.IsEmptyElement))
        {
            xpath.Pop();
        }
    }
}
于 2013-10-10T09:10:21.557 回答
1

我不知道 API,但我的猜测是否定的,您无法获得 xpath,因为验证可能被实现为有限状态机。状态可能不会转换为 xpath,或者当它对多个元素有效且找到的元素不在预期集合中时,xpath 不存在。

于 2013-02-03T23:02:36.003 回答
1

终于以这种方式成功了!

当我使用 XmlReader.Create(xmlStream, settings) 和 xmlRdr.Read() 验证 XML 时,我捕获了 ValidationEventHandler 的发送者并发现它是 {System.Xml.XsdValidatingReader} 的对象,所以我将发送者转移到xmlreader 对象,XMLReader 类中有一些函数可以帮助您找到错误属性的父节点。

有一点需要注意,当我使用 XMLReader.MoveToElement() 时,验证函数会卡在错误属性中的循环中,所以我使用了 MoveToAtrribute(AttributeName),然后使用 MoveToNextAttribute 来避免卡在循环中,也许有是更优雅的处理方式。

事不宜迟,下面是我的代码。

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;
    }
于 2016-12-06T06:40:16.240 回答
0

或者,您可以使用如何从 C# 中的行号和列号查找 XML 节点中的代码?使用 获取失败节点args.Exception.LineNumberargs.Exception.LinePosition然后根据需要导航 XML 文档,以提供有关哪些数据导致验证失败的更多信息。

于 2015-03-26T09:47:58.780 回答
0

如果像我一样,您正在使用“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) + ")");
    }
}

秘诀是获取“args”参数的“Exception”属性数据。这样做:

// 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 "";
}
于 2019-07-05T15:30:02.077 回答