デルファイのmsxmlによるスキーマ検証
質問
XMLファイルを、それが参照するスキーマに対して検証しようとしています。 (DelphiとMSXML2_TLBを使用します。)(の関連部分)コードは次のようになります:
procedure TfrmMain.ValidateXMLFile;
var
xml: IXMLDOMDocument2;
err: IXMLDOMParseError;
schemas: IXMLDOMSchemaCollection;
begin
xml := ComsDOMDocument.Create;
if xml.load('Data/file.xml') then
begin
schemas := xml.namespaces;
if schemas.length > 0 then
begin
xml.schemas := schemas;
err := xml.validate;
end;
end;
end;
これにより、キャッシュが読み込まれます( schemas.length> 0
)が、次の割り当てで例外が発生します:" XMLSchemaCache-schemacollectionsのみ使用できます"
これについてはどうすればよいですか
解決
うまくいくと思われるアプローチを思いつきました。最初にスキーマを明示的にロードしてから、themnをschemacollectionに追加します。次に、xmlファイルをロードし、schemacollectionをそのschemasプロパティに割り当てます。ソリューションは次のようになります。
uses MSXML2_TLB
That is:
// Type Lib: C:\Windows\system32\msxml4.dll
// LIBID: {F5078F18-C551-11D3-89B9-0000F81FE221}
function TfrmMain.ValidXML(
const xmlFile: String;
out err: IXMLDOMParseError): Boolean;
var
xml, xsd: IXMLDOMDocument2;
cache: IXMLDOMSchemaCollection;
begin
xsd := CoDOMDocument40.Create;
xsd.Async := False;
xsd.load('http://the.uri.com/schemalocation/schema.xsd');
cache := CoXMLSchemaCache40.Create;
cache.add('http://the.uri.com/schemalocation', xsd);
xml := CoDOMDocument40.Create;
xml.async := False;
xml.schemas := cache;
Result := xml.load(xmlFile);
if not Result then
err := xml.parseError
else
err := nil;
end;
XMLSchemaCache40以降を使用することが重要です。以前のバージョンはW3C XMLスキーマ標準に準拠していませんが、MicroSoft仕様であるXDRスキーマに対してのみ検証します。
このソリューションの欠点は、スキーマを明示的にロードする必要があることです。それらを自動的に取得することが可能であるべきであるように思えます。
他のヒント
BennyBechDkは正しい軌道に乗っているかもしれませんが、彼のコードにはいくつかの問題があり、以下で修正します:
uses Classes, XMLIntf, xmlDoc, SysUtils;
function IsValidXMLDoc(aXmlDoc: IXMLDocument): boolean;
var
validateDoc: IXMLDocument;
begin
result := false; // eliminate any sense of doubt, it starts false period.
validateDoc := TXMLDocument.Create(nil);
try
validateDoc.ParseOptions := [poResolveExternals, poValidateOnParse];
validateDoc.XML := aXmlDoc.XML;
validateDoc.Active := true;
Result := True;
except
// for this example, I am going to eat the exception, normally this
// exception should be handled and the message saved to display to
// the user.
end;
end;
システムに例外を発生させるだけであれば、そもそもそれを関数にする理由はありません。
uses Classes, XMLIntf, XMLDoc, SysUtils;
procedure ValidateXMLDoc(aXmlDoc: IXMLDocument);
var
validateDoc: IXMLDocument;
begin
validateDoc := TXMLDocument.Create(nil);
validateDoc.ParseOptions := [poResolveExternals, poValidateOnParse];
validateDoc.XML := aXmlDoc.XML;
validateDoc.Active := true;
end;
validateDocはインターフェイスであるため、関数/プロシージャが終了すると適切に破棄されるため、自分で破棄する必要はありません。 ValidateXmlDocを呼び出しても例外が発生しない場合、それは有効です。個人的には、最初の呼び出しIsValidXMLDocが好きです。IsValidXMLDocは、有効な場合はtrueを返し、そうでない場合はfalseを返します(そして、それ自体の外側で例外を発生させません)。
私はこの不満を解決するためにMielのソリューションに取り組みました。 xmlを2回開き、1回は名前空間を取得し、もう1回はスキーマコレクションを作成した後にファイルを検証します。わたしにはできる。 IXMLDOMDocument2は、一度開くとスキーマプロパティの設定を受け入れないようです。
function TForm1.ValidXML2(const xmlFile: String;
out err: IXMLDOMParseError): Boolean;
var
xml, xml2, xsd: IXMLDOMDocument2;
schemas, cache: IXMLDOMSchemaCollection;
begin
xml := CoDOMDocument.Create;
if xml.load(xmlFile) then
begin
schemas := xml.namespaces;
if schemas.length > 0 then
begin
xsd := CoDOMDocument40.Create;
xsd.Async := False;
xsd.load(schemas.namespaceURI[0]);
cache := CoXMLSchemaCache40.Create;
cache.add(schemas.namespaceURI[1], xsd);
xml2 := CoDOMDocument40.Create;
xml2.async := False;
xml2.schemas := cache;
Result := xml2.load(xmlFile);
//err := xml.validate;
if not Result then
err := xml2.parseError
else
err := nil;
end;
end;
以前に次のコードを使用してXMLドキュメントを検証しました:
Uses
Classes,
XMLIntf,
SysUtils;
Function ValidateXMLDoc(aXmlDoc: IXMLDocument): boolean;
var
validateDoc: IXMLDocument;
begin
validateDoc := TXMLDocument.Create(nil);
validateDoc.ParseOptions := [poResolveExternals, poValidateOnParse];
validateDoc.XML := aXmlDoc.XML;
validateDoc.Active := true;
Result := True;
end;