validación de esquema con msxml en delphi
xsd dtd (4)
Estoy intentando validar un archivo XML contra las referencias del esquema al que hace referencia. (Usando Delphi y MSXML2_TLB). El código (parte relevante del) se ve más o menos así:
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;
Esto tiene el resultado de que la memoria caché está cargada (schemas.length> 0), pero luego la siguiente asignación genera una excepción: "solo se pueden usar XMLSchemaCache-schemacollections".
¿Cómo debo ir sobre esto?
Gracias, Miel.
Trabajé en la solución de Miel para resolver la desventaja. Abrí el xml dos veces, una para obtener los espacios de nombres, y el otro, después de crear la colección de esquemas, para validar el archivo. Esto funciona para mi. Parece que IXMLDOMDocument2, una vez abierto, no acepta establecer la propiedad de 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;
Si bien BennyBechDk podría estar en el camino correcto, tengo algunos problemas con su código que voy a corregir a continuación:
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;
Si quería que el sistema simplemente planteara la excepción, entonces no hay razón para hacerlo funcionar en primer 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;
Debido a que validateDoc es una interfaz, se eliminará correctamente a medida que la función / procedimiento finalice, no hay necesidad de realizar la eliminación usted mismo. Si llama a ValidateXmlDoc y no obtiene una excepción, entonces es válido. Personalmente, me gusta la primera llamada, IsValidXMLDoc que devuelve verdadero si es válido o falso si no (y no genera excepciones fuera de sí mismo).
He encontrado un enfoque que parece funcionar. Primero cargo los esquemas explícitamente, luego agrego themn a schemacollection. A continuación, cargué el archivo xml y asigné el schemacollection a su propiedad schemas. La solución ahora se ve así:
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;
Es importante usar XMLSchemaCache40 o posterior. Las versiones anteriores no siguen el estándar W3C XML Schema, sino que solo validan contra XDR Schema, una especificación de MicroSoft.
La desventaja de esta solución es que necesito cargar los esquemas explícitamente. Me parece que debería ser posible recuperarlos automáticamente.
He validado documentos XML con el siguiente 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;