2015-06-04 22 views
0

我有一些Delphi代码来读取和验证基于XSD文档的XML文件。我正在使用Windows DOM(TMXLDocument)。 This Article解释了基础逻辑。使用Windows DOM和TXMLDocument验证XML:在某些计算机上不起作用

它在一些计算机上工作(即抛出违规标签的异常)。但在新电脑上它不会抛出任何异常。

在Windows中是否有设置我需要更改才能使其工作?或者任何人都知道一个原生的Delphi组件来验证XML?

XSD文件:http://www.nemsis.org/media/XSD/EMSDataSet.xsd

示例XML(注意:E02_02需要有在XSD xyz.com/DataSet.xsd

<EMSDataSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.nemsis.org" xsi:schemaLocation="http://myfakedomain.com/DataSet.xsd"> 
<Header> 
<Record> 
    <E02> 
    <E02_01>123</E02_01> 
    <E02_02>0</E02_02> 
    </E02> 
</Record> 
</Header> 
</EMSDataSet> 

Delphi代码基于正值:

XMLDoc:= TXMLDocument.Create(nil); 
try 
    XMLDoc.ParseOptions:= [poResolveExternals, poValidateOnParse]; 
    XMLDoc.LoadFromFile(filetocheck); 
    XMLDoc.Active:= True; 
except 
    on E:EDOMParseError do begin 
    showMessage(e.Message); 
    end; 
end;  

例外:

The element: '{http://www.nemsis.org}E02_02' has an invalid value according to its data type. Line: 20 <E02_02>0</E02_02> 
+3

你期待它抛出什么异常,你为什么期待它?你有没有尝试捕获'Exception'而不是'EDOMParseError'?你能否展示一个不会失败的例子,并期望它失败?另外,是什么类型的XMLDoc声明为TXMLDocument或IXMLDocument?您是否知道在创建具有'nil'所有者的'TXMLDocument'时,需要* IXMLDocument'? –

+3

顺便提一下,在这个例子中设置'Active:= True'是多余的,因为当'LoadFromFile()'出现没有错误时'Active'已经为True。 –

+0

这里没有太多东西要走。也许你可以提供XML。 –

回答

2

TXMLDocument不直接支持在使用MSXML时启用XSD验证,因此MSXML有责任对其进行管理。启用poResolveExternalspoValidateOnParse标志对此很重要,但还有其他一些因素需要考虑。最重要的是,尽管MSXML不支持引用来自XML内的XSD,但对是否在加载XML引用的XSD将实际被使用的一些限制:

Referencing XSD Schemas in Documents

要引用一个XML Schema( XSD)架构从MSXML 6.0中的XML文档,您可以使用以下任何一种方法将架构链接到XML文档,以便MSXML将使用该架构来验证文档内容。

  • 参考使用XML模式实例的XML文档中的XSD模式属性,如任一的xsi:的schemaLocation或的xsi:noNamespaceSchemaLocation。

  • 将XSD架构文件添加到架构缓存,然后在加载或解析XML文档之前将该缓存连接到DOM文档或SAX读取器。

...

xsi:schemaLocation属性效果很好在命名空间前缀显式声明的要验证XML文档中使用的情况。

以下示例显示引用外部XSD模式MyData.xsd的XML文档,用于验证映射到“MyData:”名称空间前缀的'urn:MyData'名称空间URI中的节点。

<catalog xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' 
    xsi:schemaLocation="urn:MyData http://www.example.com/MyData.xsd" 
    <MyData:book xmlns:MyData="urn:MyData"> 
    <MyData:title>Presenting XML</MyData:title> 
    <MyData:author>Richard Light</MyData:author> 
    </MyData:book> 

为了使MyData的。XSD文件与配对,并使用您验证的元素和属性,与开始节点“迈德特”,该模式需要使用并包含以下架构属性:

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
     xmlns:MyData="urn:MyData" 
     targetNamespace="urn:MyData" 
     elementFormDefault="qualified"> 

这些属性声明“瓮: MyData'命名空间URI和“MyData:”命名空间前缀,以便它们与在XML文件中进行这些声明的方式相同。 如果它们不匹配,则在验证期间永远不会调用指定位置的模式。

您还没有显示您的XSD,但是您显示的XML不符合上述文档中提到的规则。特别是,您错过了使用urn命名空间映射以及您想要验证的XML节点上的前缀。某些版本的MSXML可能比其他版本更好地处理此问题,这可以解释为什么验证适用于某些机器,并且在其他机器上被忽略,具体取决于安装的MSXML版本。

话虽这么说,你可能不得不求助于文档中提到的第二种方法:

  • XSD架构文件添加到架构缓存,然后高速缓存连接到DOM文档或SAX读取器,然后加载或解析XML文档。

这需要直接使用MSXML,你不能TXMLDocument做到这一点:

MSXML还提供了连接和使用模式缓存来存储,负载和架构连接的手段一个XML文档,如下面的VBScript代码摘录:

'Create the schema cache and add the XSD schema to it. 
set oSC = CreateObject("MSXML2.XMLSchemaCache.6.0") 
oSC.Add "urn:MyData", "http://www.example.com/MyData.xsd" 
'Create the DOM document assign the cache to its schemas property. 
set oXD = CreateObject("MSXML2.DOMDocument.6.0") 
oXD.schemas = oSC 
'Set properties, load and validate it in the XML DOM. 

的疑难杂症是,你必须知道在哪里XSD是LOCA以便将其连接到解析器。因此,您只需加载一次XML就可以提取XSD位置,然后将XSD加载到模式缓存中,然后使用附加的XSD重新加载XML。下面是一些Delphi例子:

schema validation with msxml in delphi

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; 
end; 

How to validate a IXMLDocument against a XML Schema?

unit XMLValidate; 

// Requirements ---------------------------------------------------------------- 
// 
// MSXML 4.0 Service Pack 1 
// http://www.microsoft.com/downloads/release.asp?releaseid=37176 
// 
// ----------------------------------------------------------------------------- 

interface 

uses 
    SysUtils, XMLIntf, xmldom, XMLSchema; 

type 
    EValidateXMLError = class(Exception) 
    private 
    FErrorCode: Integer; 
    FReason: string; 
    public 
    constructor Create(AErrorCode: Integer; const AReason: string); 
    property ErrorCode: Integer read FErrorCode; 
    property Reason: string read FReason; 
    end; 

procedure ValidateXMLDoc(const Doc: IDOMDocument; const SchemaLocation, SchemaNS: WideString); overload; 
procedure ValidateXMLDoc(const Doc: XMLIntf.IXMLDocument; const SchemaLocation, SchemaNS: WideString); overload; 
procedure ValidateXMLDoc(const Doc: IDOMDocument; const Schema: IXMLSchemaDoc); overload; 
procedure ValidateXMLDoc(const Doc: XMLIntf.IXMLDocument; const Schema: IXMLSchemaDoc); overload; 

implementation 

uses 
    Windows, ComObj, msxmldom, MSXML2_TLB; 

resourcestring 
    RsValidateError = 'Validate XML Error (%.8x), Reason: %s'; 

{ EValidateXMLError } 

constructor EValidateXMLError.Create(AErrorCode: Integer; const AReason: string); 
begin 
    inherited CreateResFmt(@RsValidateError, [AErrorCode, AReason]); 
    FErrorCode := AErrorCode; 
    FReason := AReason; 
end; 

{ Utility routines } 

function DOMToMSDom(const Doc: IDOMDocument): IXMLDOMDocument2; 
begin 
    Result := ((Doc as IXMLDOMNodeRef).GetXMLDOMNode as IXMLDOMDocument2); 
end; 

function LoadMSDom(const FileName: WideString): IXMLDOMDocument2; 
begin 
    Result := CoDOMDocument40.Create; 
    Result.async := False; 
    Result.resolveExternals := True; //False; 
    Result.validateOnParse := True; 
    Result.load(FileName); 
end; 

{ Validate } 

procedure InternalValidateXMLDoc(const Doc: IDOMDocument; const SchemaDoc: IXMLDOMDocument2; const SchemaNS: WideString); 
var 
    MsxmlDoc: IXMLDOMDocument2; 
    SchemaCache: IXMLDOMSchemaCollection; 
    Error: IXMLDOMParseError; 
begin 
    MsxmlDoc := DOMToMSDom(Doc); 
    SchemaCache := CoXMLSchemaCache40.Create; 
    SchemaCache.add(SchemaNS, SchemaDoc); 
    MsxmlDoc.schemas := SchemaCache; 
    Error := MsxmlDoc.validate; 
    if Error.errorCode <> S_OK then 
    raise EValidateXMLError.Create(Error.errorCode, Error.reason); 
end; 

procedure ValidateXMLDoc(const Doc: IDOMDocument; const SchemaLocation, SchemaNS: WideString); 
begin 
    InternalValidateXMLDoc(Doc, LoadMSDom(SchemaLocation), SchemaNS); 
end; 

procedure ValidateXMLDoc(const Doc: XMLIntf.IXMLDocument; const SchemaLocation, SchemaNS: WideString); 
begin 
    InternalValidateXMLDoc(Doc.DOMDocument, LoadMSDom(SchemaLocation), SchemaNS); 
end; 

procedure ValidateXMLDoc(const Doc: IDOMDocument; const Schema: IXMLSchemaDoc); 
begin 
    InternalValidateXMLDoc(Doc, DOMToMSDom(Schema.DOMDocument), ''); 
end; 

procedure ValidateXMLDoc(const Doc: XMLIntf.IXMLDocument; const Schema: IXMLSchemaDoc); 
begin 
    InternalValidateXMLDoc(Doc.DOMDocument, DOMToMSDom(Schema.DOMDocument), ''); 
end; 

end. 

Doc := LoadXMLData(XmlFileEdit.Lines.Text); 
ValidateXMLDoc(Doc, FSchemaFileName, 'http://www.foo.com'); 

XML Documents, Schemas and Validation

var 
    XML, XSDL: Variant; 
begin 
    XSDL := CreateOLEObject('MSXML2.XMLSchemaCache.4.0'); 
    XSDL.validateOnLoad := True; 
    XSDL.add('','MySchema.xsd'); // 1st argument is target namespace 
    ShowMessage('Schema Loaded'); 
    XML := CreateOLEObject('MSXML2.DOMDocument.4.0'); 
    XML.validateOnParse := True; 
    XML.resolveExternals := True; 
    XML.schemas := XSDL; 
    XML.load('file.xml'); 
    ShowMessage(XML.parseError.reason); 
end. 
+0

在delphi **实现中尝试使用msxml进行**模式验证。我必须修改** xml:= CoDOMDocument.Create; **以在delphi **中使用msxml进行**模式验证。当我跑我得到“类未注册”。我在电脑上注册了** msxml6.dll **。 –

相关问题