Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何获取XML模式验证失败位置的XPath(或节点)?_C#_.net_Linq To Xml_Xmldocument_Xml Validation - Fatal编程技术网

C# 如何获取XML模式验证失败位置的XPath(或节点)?

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

我正在使用(它的功能似乎与XmlDocument.Validate相同)根据XSD验证XML文档-这很有效,并且我知道验证错误

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

  • 错误消息(即“X”属性无效-值“Y”根据其数据类型“Z”无效-模式约束失败”)
  • 严重性
我希望得到验证失败的“失败的XPath”(在有意义的地方):也就是说,我希望得到与XML文档相关的失败(与XML文本相反)

是否有方法从
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"?
});

更新:我找到了指向()的链接,从中我确定了两件事:

  • XmlSchemaException可以专用于具有SourceObject属性的对象-但是,在验证过程中始终返回null:“当验证XmlReader对象在验证过程中抛出XmlSchemaValidationException时,SourceObject属性的值为null”

  • 我可以通读文档(通过
    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 "";
    }