convalida dello schema con msxml in delphi
Domanda
Sto cercando di convalidare un file XML rispetto agli schemi a cui fa riferimento. (Uso di Delphi e MSXML2_TLB.) Il codice (parte rilevante del) ha un aspetto simile al seguente:
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;
Questo ha il risultato che la cache è caricata ( schemas.length > 0
), ma poi il prossimo incarico solleva un'eccezione: " è possibile utilizzare solo XMLSchemaCache-schemacollections. "
Come devo procedere?
Soluzione
Ho escogitato un approccio che sembra funzionare. Prima carico lo schema in modo esplicito, quindi li aggiungo allo schema di raccolta. Successivamente carico il file xml e assegno schemacollection alla sua proprietà schemi. La soluzione ora appare così:
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;
È importante utilizzare XMLSchemaCache40 o successivo. Le versioni precedenti non seguono lo standard W3C XML Schema, ma convalidano solo contro XDR Schema, una specifica MicroSoft.
Lo svantaggio di questa soluzione è che devo caricare esplicitamente lo schema. Mi sembra che dovrebbe essere possibile recuperarli automaticamente.
Altri suggerimenti
Mentre BennyBechDk potrebbe essere sulla buona strada, ho alcuni problemi con il suo codice che correggerò di seguito:
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;
Se si desidera che il sistema sollevi solo l'eccezione, non c'è motivo di renderlo una funzione in primo luogo.
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;
Poiché validateDoc è un'interfaccia, verrà eliminata correttamente all'uscita dalla funzione / procedura, non è necessario eseguire lo smaltimento da soli. Se si chiama ValidateXmlDoc e non si ottiene un'eccezione, è valido. Personalmente mi piace la prima chiamata, IsValidXMLDoc che restituisce true se valido o false in caso contrario (e non genera eccezioni al di fuori di se stesso).
Ho lavorato sulla soluzione di Miel per risolvere il disavventura. Apro il file XML due volte, una volta per ottenere gli spazi dei nomi e l'altro, dopo aver creato la raccolta di schemi, per convalidare il file. Per me funziona. Sembra che IXMLDOMDocument2, una volta aperto, non accetta di impostare la proprietà degli schemi.
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;
Ho precedentemente convalidato documenti XML utilizzando il seguente codice:
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;