esquema de validação com msxml em delphi
Pergunta
Eu estou tentando validar um arquivo XML contra os esquemas faz referência. (Usando Delphi e MSXML2_TLB). O (parte relevante do) Código é algo como isto:
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;
Isto tem o resultado que o cache é carregado (schemas.length > 0
), mas, em seguida, a próxima missão levanta uma exceção: "apenas XMLSchemaCache-schemacollections pode ser usado"
Como devo fazer isso?
Solução
Eu vim com uma abordagem que parece funcionar. A primeira vez que carregar o esquema de forma explícita, em seguida, adicione themn ao schemacollection. Em seguida eu carregar o arquivo xml e atribuir o schemacollection à sua propriedade esquemas. A solução agora se parece com isso:
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 usar XMLSchemaCache40 ou mais tarde. Versões anteriores não seguem o padrão W3C XML Schema, mas apenas validar contra a XDR esquema, uma especificação da Microsoft.
A desvantagem desta solução é que eu preciso para carregar o esquema é explicitamente. Parece-me que deveria ser possível recuperá-los automaticamente.
Outras dicas
Enquanto BennyBechDk pode estar no caminho certo, eu tenho alguns problemas com o código que eu estou indo para corrigir abaixo:
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 você queria que o sistema apenas aumentar a exceção, então não há razão para torná-lo uma função em primeiro lugar.
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;
Porque validateDoc é uma interface, ela será eliminada de forma adequada como as saídas função / procedimento, não há necessidade de realizar o descarte si mesmo. Se você chamar ValidateXmlDoc e não obter uma exceção, então é válido. Pessoalmente, como a primeira chamada, IsValidXMLDoc que retorna verdadeiro se válido ou falso se não (e não gerar exceções fora de si mesmo).
Eu trabalhei na solução Miel's para resolver o disadventage. Abro a xml duas vezes, uma para obter os namespaces, e o outro, depois de criar a coleção de esquema, para validar o arquivo. Funciona para mim. Parece que a IXMLDOMDocument2, uma vez aberto, NÃO FAZEM aceita definir a propriedade esquemas.
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;
Já anteriormente validado documentos XML usando o seguinte código:
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;