问题描述
我有一些Delphi代码可以读取和验证基于XSD文档的XML文件。我正在使用Windows DOM(TMXLDocument)。 解释了基础逻辑。
它适用于某些计算机(即抛出违规标签的异常)。但是在一台较新的计算机上,它不会有任何异常。
Windows中是否有一个设置需要更改以使其正常工作?或者任何人知道一个本地Delphi组件来验证XML?
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
TXMLDocument
不直接支持在使用MSXML时启用XSD验证,因此MSXML有责任对其进行管理。启用 poResolveExternals
和 poValidateOnParse
标志对此很重要,但还有一些其他因素要考虑。最重要的是,虽然MSXML确实支持从XML引用XSD,但它在加载XML时是否实际使用引用的XSD有一些限制:
要从MSXML 6.0中的XML文档引用XML模式(XSD)架构,您可以使用以下方法之一将模式链接到XML文档,以便MSXML将使用模式来验证文档内容。
-
使用XML模式实例属性(如xsi:schemaLocation或xsi:noNamespaceSchemaLocation)引用XML文档中的XSD模式。 / p>
-
将XSD架构文件添加到模式缓存中,然后在加载或解析XML文档之前将该缓存连接到DOM文档或SAX读取器。 p>
...
属性在名称空间前缀被明确声明并在XML文档中使用的情况下运行良好想要验证。
以下示例显示了引用外部XSD架构的XML文档,MyData.xsd用于验证位于urn:MyData命名空间URI,映射到MyData:命名空间前缀。
< catalog xmlns:xsi ='http: /www.w3.org/2001/XMLSchema-实例'
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。
就是说,您可能需要诉诸文档中提到的第二种方法:
这需要直接使用MSXML,您不能使用 TXMLDocument
:
你知道,你必须知道XSD在哪里以便将其连接到解析器。因此,您只需要加载XML即可提取XSD位置,然后将XSD加载到模式高速缓存中,然后使用附加的XSD重新加载XML。这里有一些Delphi的例子:
函数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然后
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;
结束
结束
结束
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);超载;
程序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');
var
XML,XSDL:Variant ;
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
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:
That requires using MSXML directly, you can't do it with TXMLDocument
:
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:在某些计算机上无效的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!