Pregunta

Estoy intentando validar un archivo XML contra los esquemas que hace referencia. (Usando Delphi y MSXML2_TLB.) El código (parte relevante del) se parece a esto:

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 se carga la memoria caché ( schemas.length > 0 ), pero luego la siguiente asignación genera una excepción: " solo se pueden usar las colecciones de esquemas XMLSchemaCache. "

¿Cómo debo hacer esto?

¿Fue útil?

Solución

Se me ocurrió un enfoque que parece funcionar. Primero cargo los esquemas explícitamente, luego los agrego a la colección de esquemas. A continuación, cargo el archivo xml y asigno la colección de esquemas a su propiedad de esquemas. 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 del esquema XML W3C, sino que solo se validan contra el esquema XDR, una especificación de MicroSoft.

La desventaja de esta solución es que necesito cargar el esquema explícitamente. Me parece que debería ser posible recuperarlos automáticamente.

Otros consejos

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 desea que el sistema solo genere 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 a que validateDoc es una interfaz, se eliminará correctamente a medida que la función / procedimiento finalice, no es necesario 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).

Trabajé en la solución de Miel para resolver la desventaja. Abro el xml dos veces, una para obtener los espacios de nombres, y la otra, 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;

He validado previamente 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;
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top