使用Windows DOM和TXMLDocument验证XML:在某些计算机上不起作用 [英] Validate XML using Windows DOM and TXMLDocument: doesn't work on some computers

查看:140
本文介绍了使用Windows DOM和TXMLDocument验证XML:在某些计算机上不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一些Delphi代码可以读取和验证基于XSD文档的XML文件。我正在使用Windows DOM(TMXLDocument)。 本条解释了基础逻辑。



它适用于某些计算机(即抛出违规标记的异常)。但是在一台较新的计算机上,它不会发生任何异常。



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-instancexmlns =http://www.nemsis.orgxsi: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(filetoche CK);
XMLDoc.Active:= True;
除了
在E上:EDOMParseError do begin
showMessage(e.Message);
结束
结束

异常:

 code>元素:'{http://www.nemsis.org} E02_02'根据其数据类型具有无效值。线:20  0  


解决方案

TXMLDocument 不直接支持在使用MSXML时启用XSD验证,因此MSXML有责任对其进行管理。启用 poResolveExternals poValidateOnParse 标志对此很重要,但还有一些其他因素需要考虑。最重要的是,虽然MSXML确实支持在XML中引用XSD,但它在加载XML时是否实际使用引用的XSD有一些限制:



在文档中引用XSD模式


要从MSXML 6.0中的XML文档引用XML模式(XSD)模式,可以使用以下方法之一将模式链接到XML文档,以便MSXML将使用验证文档内容的模式。




  • 使用XML模式实例属性(如xsi:schemaLocation或xsi:noNamespaceSchemaLocation)引用XML文档中的XSD模式。 / p>


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




...



xsi:schemaLocation 属性在名称空间前缀被明确声明并在XML文档中使用的情况下运行良好您要验证。



以下示例显示了引用外部XSD架构的XML文档,MyData.xsd用于验证位于urn:MyData中的节点'命名空间URI映射到MyData:命名空间前缀。

 < 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>呈现XML< / MyData:title>
< MyData:author> Richard Light< / MyData:author>
< / MyData:book>



为了MyData。 xsd文件与之配对,并用于验证以MyData:开头的元素和属性节点,架构需要使用并包含以下模式属性:

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

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


您尚未显示您的XSD,但您显示的XML不符合上述文档中提到的规则。特别是,您缺少使用 urn 命名空间映射,并且要在要验证的XML节点上使用前缀。某些版本的MSXML可能比其他版本更好,这可以解释为什么验证在某些机器上工作,并且在其他机器上被忽略,这取决于安装了哪些版本的MSXML。



就是说,您可能需要诉诸文档中提到的第二种方法:



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


这需要直接使用MSXML,您不能使用 TXMLDocument


MSXML还提供了连接和使用模式缓存来存储,加载和连接模式到XML文档的方法,例如以下VBScript代码摘录:

 '创建模式缓存并将XSD模式添加到它。 
set oSC = CreateObject(MSXML2.XMLSchemaCache.6.0)
oSC.Addurn:MyData,http://www.example.com/MyData.xsd
'创建DOM文档将缓存分配给其schemas属性。
set oXD = CreateObject(MSXML2.DOMDocument.6.0)
oXD.schemas = oSC
'在XML DOM中设置属性,加载和验证它。


你知道,你必须知道XSD在哪里以便将其连接到解析器。因此,您只需要加载XML即可提取XSD位置,然后将XSD加载到模式缓存中,然后使用附加的XSD重新加载XML。这里有一些Delphi的例子:



在delphi中使用msxml进行模式验证

 函数TForm1.ValidXML2(const xmlFile:String; 
out err:IXMLDOMParseError):Boolean;
var
xml,xml2,xsd:IXMLDOMDocument2;
schemas,cache:IXMLDOMSchemaCollection;
begin
xml:= CoDOMDocument.Create;
如果xml.load(xmlFile)然后
begin
schemas:= xml.namespaces;
如果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;
结果:= xml2.load(xmlFile);
// err:= xml.validate;
如果没有结果然后
err:= xml2.parseError
else
err:= nil;
结束
结束
结束

如何根据XML模式验证IXMLDocument?

  unit XMLValidate; 

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

接口

使用
SysUtils,XMLIntf,xmldom,XMLSchema;

type
EValidateXMLError = class(Exception)
private
FErrorCode:Integer;
FReason:string;
public
构造函数Create(AErrorCode:Integer; const AReason:string);
属性ErrorCode:整数读取FErrorCode;
属性原因:string read FReason;
结束

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

实现

使用
Windows,ComObj,msxmldom,MSXML2_TLB;

resourcestring
RsValidateError ='验证XML错误(%.8x),原因:%s';

{EValidateXMLError}

构造函数EValidateXMLError.Create(AErrorCode:Integer; const AReason:string);
begin
继承CreateResFmt(@RsValidateError,[AErrorCode,AReason]);
FErrorCode:= AErrorCode;
FReason:= AReason;
结束

{实用程序}

函数DOMToMSDom(const Doc:IDOMDocument):IXMLDOMDocument2;
begin
结果:=((Doc as IXMLDOMNodeRef).GetXMLDOMNode as IXMLDOMDocument2);
结束

函数LoadMSDom(const FileName:WideString):IXMLDOMDocument2;
begin
结果:= CoDOMDocument40.Create;
Result.async:= False;
Result.resolveExternals:= True; //假;
Result.validateOnParse:= True;
Result.load(FileName);
结束

{验证}

程序InternalValidateXMLDoc(const Doc:IDOMDocument; const SchemaDoc:IXMLDOMDocument2; const SchemaNS:WideString);
var
MsxmlDoc:IXMLDOMDocument2;
SchemaCache:IXMLDOMSchemaCollection;
错误:IXMLDOMParseError;
begin
MsxmlDoc:= DOMToMSDom(Doc);
SchemaCache:= CoXMLSchemaCache40.Create;
SchemaCache.add(SchemaNS,SchemaDoc);
MsxmlDoc.schemas:= SchemaCache;
错误:= MsxmlDoc.validate;
如果Error.errorCode<> S_OK然后
raise EValidateXMLError.Create(Error.errorCode,Error.reason);
结束

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

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

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

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

结束。

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

XML文档,模式和验证

  var 
XML,XSDL:变体;
begin
XSDL:= CreateOLEObject('MSXML2.XMLSchemaCache.4.0');
XSDL.validateOnLoad:= True;
XSDL.add('','MySchema.xsd'); //第一个参数是目标命名空间
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);
结束。


I have some Delphi code to read and validates XML files based on an XSD document. I am using using Windows DOM (TMXLDocument). This Article explains the underlying logic.

It works on some computers (i.e. throws exception for offending tags). But on a newer computer it does not throw any exception.

Is there a setting in Windows I need to change to get it to work? Or anyone know of a native Delphi component to validate XML?

XSD File: http://www.nemsis.org/media/XSD/EMSDataSet.xsd

Sample XML (note E02_02 is required to have a positive value based on at 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 Code:

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;    

Exception:

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

解决方案

TXMLDocument does not directly support enabling XSD validation when using MSXML, so it is MSXML's responsibility to manage it. Enabling the poResolveExternals and poValidateOnParse flags is important for that, but there are some other factors to consider. Most importantly, although MSXML does support referencing an XSD from within the XML, it has some limitations on whether the referenced XSD will actually be used while loading the XML:

Referencing XSD Schemas in Documents

To reference an XML Schema (XSD) schema from an XML document in MSXML 6.0, you can use any one of the following means to link a schema to an XML document so that MSXML will use the schema to validate the document contents.

  • Reference the XSD schema in the XML document using XML schema instance attributes such as either xsi:schemaLocation or xsi:noNamespaceSchemaLocation.

  • Add the XSD schema file to a schema cache and then connect that cache to the DOM document or SAX reader, prior to loading or parsing the XML document.

...

The xsi:schemaLocation attribute works well in situations where namespace prefixes are explicitly declared and used in the XML document you want to validate.

The following example shows an XML document that references an external XSD schema, MyData.xsd for us in validating nodes that are in the 'urn:MyData' namespace URI , which is mapped to the "MyData:" namespace prefix.

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

In order for the MyData.xsd file to be paired with and used you to validate elements and attribute nodes that start with the "MyData:", the schema needs to use and contain the following schema attributes:

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

These attributes declare the 'urn:MyData' namespace URI and the "MyData:" namespace prefix so that they correspond identically to how these declarations were made in the XML file. If they do not match, the schema at the specified location would never be invoked during validation.

You have not shown your XSD yet, but the XML you have shown does not conform to the rules mentioned in the above documentation. In particular, you are missing the use of a urn namespace mapping, and prefixes on the XML nodes that you want to validate. Some versions of MSXML might handle this better than others, which could explain why the validation works on some machines and is ignored on other machines, depending on which versions of MSXML are installed.

That being said, you may have to resort to the second approach mentioned in the documentation:

  • Add the XSD schema file to a schema cache and then connect that cache to the DOM document or SAX reader, prior to loading or parsing the XML document.

That requires using MSXML directly, you can't do it with TXMLDocument:

MSXML also provides a means to connect and use a schema cache to store, load and connect a schema to an XML document, such as in the following VBScript code excerpt:

'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.

The gotcha is that you have to know where the XSD is located in order to hook it up to the parser. So, you would have to load the XML once just to extract the XSD location, then load the XSD into an schema cache, and then re-load the XML with the XSD attached. Here are some Delphi examples of that:

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.

这篇关于使用Windows DOM和TXMLDocument验证XML:在某些计算机上不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆