8

XSD に対して XML ドキュメントを検証するためにXDocument.Validate (XmlDocument.Validate と同じように機能するようです) を使用しています。これはうまく機能し、検証エラーが通知されます。

ただし、一部の情報のみが [確実に] ValidationEventHandler (および XmlSchemaException) で公開されているようです。

  • エラーメッセージ (つまり、「'X' 属性が無効です - 値 'Y' はそのデータ型 'Z' に従って無効です - パターン制約に失敗しました」)、
  • 重大度

が望むのは、検証の失敗の「失敗した XPath」を取得することです (それが理にかなっています)。つまり、(XML テキストではなく) XML ドキュメントに関連して失敗を取得したいと考えています。

から「失敗した XPath」情報を取得する方法はありXDocument.Validateますか? XmlValidatingReaderそうでない場合、 1などの別の XML 検証メソッドを使用して「失敗した XPath」を取得できますか?


バックグラウンド:

JSON から XML への自動変換 (JSON.NET 経由) により、XML はデータとして Web サービスに送信されます。このため、元の JSON データのために順序が保証されていないテキストではなく、XDocument データ1の処理を​​開始します。REST クライアントは、詳しくは触れたくありませんが、基本的には XML ドキュメントに対する HTML フォーム フィールドのラッパーであり、サーバーでの検証は XML スキーマの検証とビジネス ルールの検証の 2 つの部分で行われます。

ビジネス ルールの検証では、適合に失敗したフィールドの "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"?
});

更新: 「ドキュメントの検証中に XML スキーマ情報にアクセスする」 ( cached ) にリンクするXDocument を検証するときにスキーマ情報をキャプチャすることがわかりました。

  1. XmlSchemaExceptionは、SourceObject プロパティを持つXmlSchemaValidationExceptionに特化できますが、これは検証中に常に 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() を使用すると、Validation 関数がエラー属性のループにスタックすることに注意する必要があるため、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.LineNumberを使用して失敗したノードを取得し、args.Exception.LinePosition必要に応じて XML ドキュメントをナビゲートして、検証の失敗の原因となったデータに関する詳細情報を提供します。

于 2015-03-26T09:47:58.780 に答える