同じ問題を解決しようとしています。私はかなりきれいな解決策だと思うものを思いつきました。わかりやすくするために、入力パラメーターの検証を省略しました。
まず、シナリオ: ファイルを受け取る Web サービスがあります。このファイルは、「整形式」の xml であり、XSD に対して有効である必要があります。もちろん、私たちは「適切な形式」を信頼していませんし、「私たちが知っている」ことが正しいという XSD に対して有効であることも信頼していません。
そのような webservice メソッドのコードを以下に示します。一目瞭然だと思います。
重要な点は、検証が行われる順序です。ロードする前に名前空間をチェックするのではなく、後でチェックしますが、きれいにチェックします。
ほとんどのファイルが「適切」であることが予想され、それがフレームワークの処理方法であるため (したがって、私はそれに抵抗しません)、いくつかの例外処理を行うことができると判断しました。
private DataTable xmlErrors;
[WebMethod]
public string Upload(byte[] f, string fileName) {
string ret = "This will have the response";
// this is the namespace that we want to use
string xmlNs = "http://mydomain.com/ns/upload.xsd";
// you could put a public url of xsd instead of a local file
string xsdFileName = Server.MapPath("~") + "//" +"shiporder.xsd";
// a simple table to store the eventual errors
// (more advanced ways possibly exist)
xmlErrors = new DataTable("XmlErrors");
xmlErrors.Columns.Add("Type");
xmlErrors.Columns.Add("Message");
try {
XmlDocument doc = new XmlDocument(); // create a document
// bind the document, namespace and xsd
doc.Schemas.Add(xmlNs, xsdFileName);
// if we wanted to validate if the XSD has itself XML errors
// doc.Schemas.ValidationEventHandler +=
// new ValidationEventHandler(Schemas_ValidationEventHandler);
// Declare the handler that will run on each error found
ValidationEventHandler xmlValidator =
new ValidationEventHandler(Xml_ValidationEventHandler);
// load the document
// will trhow XML.Exception if document is not "well formed"
doc.Load(new MemoryStream(f));
// Check if the required namespace is present
if (doc.DocumentElement.NamespaceURI == xmlNs) {
// Validate against xsd
// will call Xml_ValidationEventHandler on each error found
doc.Validate(xmlValidator);
if (xmlErrors.Rows.Count == 0) {
ret = "OK";
} else {
// return the complete error list, this is just to proove it works
ret = "File has " + xmlErrors.Rows.Count + " xml errors ";
ret += "when validated against our XSD.";
}
} else {
ret = "The xml document has incorrect or no namespace.";
}
} catch (XmlException ex) {
ret = "XML Exception: probably xml not well formed... ";
ret += "Message = " + ex.Message.ToString();
} catch (Exception ex) {
ret = "Exception: probably not XML related... "
ret += "Message = " + ex.Message.ToString();
}
return ret;
}
private void Xml_ValidationEventHandler(object sender, ValidationEventArgs e) {
xmlErrors.Rows.Add(new object[] { e.Severity, e.Message });
}
さて、xsd には次のようなものがあります。
<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="shiporder"
targetNamespace="http://mydomain.com/ns/upload.xsd"
elementFormDefault="qualified"
xmlns="http://mydomain.com/ns/upload.xsd"
xmlns:mstns="http://mydomain.com/ns/upload.xsd"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
>
<xs:simpleType name="stringtype">
<xs:restriction base="xs:string"/>
</xs:simpleType>
...
</xs:schema>
そして、「良い」XML は次のようになります。
<?xml version="1.0" encoding="utf-8" ?>
<shiporder orderid="889923" xmlns="http://mydomain.com/ns/upload.xsd">
<orderperson>John Smith</orderperson>
<shipto>
<names>Ola Nordmann</names>
<address>Langgt 23</address>
「不適切な形式の XML」、「XSD による無効な入力」、「不適切な名前空間」をテストしました。
参照:
メモリストリームから読み取る
ウェルフォームの例外処理チェックを回避しようとしています
XSD に対する検証、エラーのキャッチ
インライン スキーマ検証に関する興味深い投稿
こんにちはマーティン、コメントセクションは私の答えには短すぎるので、ここでそれを与えます。それは完全な答えであるかもしれません。一緒に改善しましょう:)
次のテストを行いました。
- テスト: xmlns="blaa"
- 結果: 名前空間が間違っているため、ファイルは拒否されます。
- テスト: xmlns="http://mydomain.com/ns/upload.xsd" および xmlns:a="blaa" で、要素に "a:someElement" が含まれている
- 結果: ファイルは、「a:someElement」を想定していないというエラーを返します。
- テスト: xmlns="http://mydomain.com/ns/upload.xsd" および xmlns:a="blaa" で、要素に「someElement」があり、必要な属性が欠落している
- 結果: ファイルは、属性が欠落しているというエラーを返します
従った戦略(私が好む)は、ドキュメントが準拠していない場合は受け入れず、理由に関する情報を提供することでした(たとえば、「間違った名前空間」)。
この戦略は、あなたが以前に言ったことに反しているようです。
ただし、お客様が送信された XML で名前空間の宣言を見逃した場合でも、それを検証できることをお伝えしたいと思います。「失敗したから直せ!」と言いたいわけではありません。
この場合、XML で定義された名前空間を無視できるようです。そのためには、正しい名前空間の検証をスキップします。
...
// Don't Check if the required namespace is present
//if (doc.DocumentElement.NamespaceURI == xmlNs) {
// Validate against xsd
// will call Xml_ValidationEventHandler on each error found
doc.Validate(xmlValidator);
if (xmlErrors.Rows.Count == 0) {
ret = "OK - is valid against our XSD";
} else {
// return the complete error list, this is just to proove it works
ret = "File has " + xmlErrors.Rows.Count + " xml errors ";
ret += "when validated against our XSD.";
}
//} else {
// ret = "The xml document has incorrect or no namespace.";
//}
...
他のアイデア...
これと並行して、提供された名前空間を独自のものにdoc.DocumentElement.NamespaceURI = "mySpecialNamespace"
置き換えるには、ルート要素の名前空間を置き換えるように設定できます。
参考:
複数の名前空間をルート要素に追加