2015-06-04 27 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 **。 –

相關問題