在Delphi中使用MSXML进行模式验证

7

我正在尝试使用Delphi和MSXML2_TLB对XML文件进行验证,以确保其符合所引用的模式。相关代码如下:

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;

这样做的结果是缓存被加载 (schemas.length > 0),但接下来的赋值会引发异常:"只能使用XMLSchemaCache-schemacollections"。

我该怎么处理?

4个回答

7

我想出了一种看起来可行的方法。首先,我明确加载模式,然后将它们添加到模式集合中。接下来,我加载xml文件并将模式集合分配给其模式属性。解决方案现在如下所示:

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;

使用XMLSchemaCache40或更高版本非常重要。早期版本不遵循W3C XML Schema标准,而只能针对XDR Schema(一种微软规范)进行验证。

这种解决方案的缺点是我需要显式地加载模式。我认为应该可以自动检索它们。


抱歉,我认为应该是 XML,而不是 xmlDoc。在编辑之前,我会再次确认。 - Miel

1

虽然BennyBechDk可能走在正确的轨道上,但我对他的代码有一些问题,我将在下面进行更正:

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;

如果您只希望系统引发异常,那么没有必要将其首先定义为函数。
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;

因为validateDoc是一个接口,所以它将在函数/过程退出时被正确处理,无需自行处理。如果您调用ValidateXmlDoc并且没有收到异常,则表示它是有效的。个人而言,我喜欢第一种调用方式,IsValidXMLDoc会返回true(如果有效)或false(如果无效),并且不会在其外部引发异常。


我无法让它工作。对于TXMLDocument,我收到了“未声明的标识符”错误。我需要导入除msxml之外的其他内容才能使其工作吗? - Miel
这根本不会对XML文档进行XSD模式验证。即使是像<xml />这样的XML内容,它也不会抛出任何异常或其他东西。 - Günther the Beautiful

1
我正在处理Miel的解决方案以解决缺点。我打开XML两次,一次是为了获取命名空间,另一次是在创建模式集合后验证文件。 对我而言它起作用了。 似乎IXMLDOMDocument2一旦打开,就无法接受设置模式属性。
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;

0

我曾经使用以下代码验证XML文档:

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;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接