2009-01-15 57 views
5

Estoy tratando de validar un archivo XML contra el esquema al que hace referencia. (El uso de Delphi y MSXML2_TLB.) El (parte pertinente de los) código es como la siguiente:validación de esquema con msxml en delphi

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 la consecuencia de que se carga la memoria caché (schemas.length> 0), pero entonces el siguiente asignación plantea una excepción: "solo se pueden usar XMLSchemaCache-schemacollections".

¿Cómo debo hacer esto?

Gracias, Miel.

Respuesta

5

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.

+0

¿cómo se define xmlDoc? – neves

+0

lo siento, creo que debería ser xml, no xmlDoc. Solo para asegurarme de que revisaré antes de editar. – Miel

0

he validado previamente los documentos XML utilizando 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; 
1

Mientras 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 aumentara la excepción, entonces no hay ninguna razón para que sea una función 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 validateDoc es una interfaz, será eliminada de forma adecuada como las salidas de la función/procedimiento, no hay necesidad de llevar a cabo la eliminación de sí 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).

+0

No puedo hacer que esto funcione. Aparece un error de "identificador no declarado" para TXMLDocument. ¿Debo importar algo que no sea msxml para que funcione? – Miel

+0

También debe usar la unidad XMLDoc. –

1

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; 
Cuestiones relacionadas