当前位置: 首页 > 知识库问答 >
问题:

使用Windows DOM和TXMLDocument验证XML:在某些计算机上不起作用

龙俭
2023-03-14

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

它在某些计算机上工作(例如,对有问题的标记抛出异常)。但在较新的计算机上,它不会引发任何异常。

Windows中是否有需要更改才能使其正常工作的设置?或者有人知道用于验证XML的本机Delphi组件吗?

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

示例XML(注意E02\U 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>

德尔福代码:

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>

共有2个答案

拓拔霄
2023-03-14

我知道这个问题是为Delphi标记的,但我认为一些Embarcadero C Builder用户可能会从使用MSXML2 OLE对象的Remy最后一个示例的C实现中受益。

我知道我希望有人能在几天前发布这个。XD公司

. h文件:

//------------------------------------------------------------------------------
#ifndef XmlValidatorUH
#define XmlValidatorUH
//------------------------------------------------------------------------------
class PACKAGE TXmlValidator
{
private:
    Variant FSchemaCache;
    Variant FXmlDomDoc;

    // TAutoCmd Variables
    Procedure   CacheProcAdd;
    PropertySet CacheSetValidateOnLoad;

    Procedure   XmlProcLoadXml;
    PropertySet XmlSetValidateOnParse;
    PropertySet XmlSetResolveExternals;
    PropertySet XmlSetSchemas;
    PropertyGet XmlGetParseError;

    PropertyGet ParseErrorGetReason;

public:
    __fastcall TXmlValidator( String _SchemaLocation );

    String __fastcall ValidationError( String _Xml );

};
//------------------------------------------------------------------------------

#endif

. cpp文件:

//------------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
//------------------------------------------------------------------------------
#include "XmlValidatorU.h"
#include <System.Win.ComObj.hpp>
//------------------------------------------------------------------------------
#pragma package(smart_init)
//------------------------------------------------------------------------------
// Validates XML against Schema
//------------------------------------------------------------------------------
// This class uses OLE objects from MSXML2 to validate XML from an XSD file.
// Generally, use the following steps to deal with OLE objects:
//  1. Define a Variant variable for your OLE Object; assign using CreateOleObject().
//  2. Define your TAutoCmd objects that will be used in Variant.Exec()
//  3. Set TAutoCmd args using << to add settings
//  4. Once everything is set up, call Exec() on your OLE Object variant
// More documentation on OLE objects / TAutoCmd at:
//  http://docwiki.embarcadero.com/CodeExamples/Rio/en/AutoCmd_(C%2B%2B)
//------------------------------------------------------------------------------
// This macro clarifies that we're registering OLE Function names to our defined TAutoCmd variables.
//
#define RegisterAutoCmd( _AutoCmd, _OleFunc ) _AutoCmd( _OleFunc )
//------------------------------------------------------------------------------
// These macros clear AutoCmdArgs before setting them.
// I made these because setting an arg multiple times just stacks them up, changing the function signature.
// Then, OLE throws a "Member Not Found" error because it can't find a function with that signature.
//
#define AutoCmdArg( _AutoCmd, _Arg ) _AutoCmd.ClearArgs(); _AutoCmd << _Arg
#define AutoCmdArgs( _AutoCmd, _Arg1, _Arg2 ) AutoCmdArg( _AutoCmd, _Arg1 ); _AutoCmd << _Arg2
//------------------------------------------------------------------------------
__fastcall TXmlValidator::TXmlValidator( String _SchemaLocation )
    :
    RegisterAutoCmd( CacheProcAdd,              "add"               ),
    RegisterAutoCmd( CacheSetValidateOnLoad,    "validateOnLoad"    ),
    RegisterAutoCmd( XmlProcLoadXml,            "loadXML"           ),
    RegisterAutoCmd( XmlSetValidateOnParse,     "validateOnParse"   ),
    RegisterAutoCmd( XmlSetResolveExternals,    "resolveExternals"  ),
    RegisterAutoCmd( XmlSetSchemas,             "schemas"           ),
    RegisterAutoCmd( XmlGetParseError,          "parseError"        ),
    RegisterAutoCmd( ParseErrorGetReason,       "reason"            )
{
    if ( _SchemaLocation.IsEmpty() ) 
    { 
        throw Exception( String( __FUNC__ ) + " - Missing Schema Location" );
    }

    // Instantiate the OLE objects
    FSchemaCache    = CreateOleObject( "MSXML2.XMLSchemaCache.4.0"  );
    FXmlDomDoc      = CreateOleObject( "MSXML2.DOMDocument.4.0"     );

    // Set static args that shouldn't change
    AutoCmdArg( CacheSetValidateOnLoad, true );
    AutoCmdArg( XmlSetValidateOnParse,  true );
    AutoCmdArg( XmlSetResolveExternals, true );

    const AnsiString NoNameSpace = "";
    AutoCmdArgs( CacheProcAdd, NoNameSpace, AnsiString( _SchemaLocation ) );

    // Load Cache
    FSchemaCache.Exec( CacheSetValidateOnLoad   );  // Validate on Load
    FSchemaCache.Exec( CacheProcAdd             );  // Add Schema file location to the cache

    // Now that the cache is loaded, set cached schema as arg to XML
    AutoCmdArg( XmlSetSchemas, FSchemaCache );
}
//------------------------------------------------------------------------------
String __fastcall TXmlValidator::ValidationError( String _Xml )
{
    AutoCmdArg( XmlProcLoadXml, AnsiString( _Xml ) );

    FXmlDomDoc.Exec( XmlSetValidateOnParse  );
    FXmlDomDoc.Exec( XmlSetResolveExternals );
    FXmlDomDoc.Exec( XmlSetSchemas          );
    FXmlDomDoc.Exec( XmlProcLoadXml         );

    Variant ParseErr = FXmlDomDoc.Exec( XmlGetParseError );

    return ParseErr.Exec( ParseErrorGetReason );
}
//------------------------------------------------------------------------------
熊朝
2023-03-14

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

在文档中引用XSD模式

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

>

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

...

在您要验证的XML文档中显式声明和使用命名空间前缀的情况下,xsi: SchemaLocat属性可以很好地工作。

以下示例显示了引用外部XSD模式MyData的XML文档。xsd用于验证“urn:MyData”命名空间URI中的节点,该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>Presenting 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代码摘录中:

'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连接到解析器,您必须知道XSD的位置。因此,只需加载一次XML即可提取XSD位置,然后将XSD加载到模式缓存中,然后重新加载附加了XSD的XML。以下是一些Delphi示例:

delphi中msxml的模式验证

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;

如何根据XML模式验证IXMLDocument?

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文档、模式和验证

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.
 类似资料:
  • 问题内容: 我已经构建了一个默认情况下使用SQL Server CE 4.0的应用程序,但也可以使用单独的SQL Server(SQL Server Express)。 我已经将它部署到办公室周围的大约10台计算机上,并且除了其中2台之外,它工作正常:副总裁和质量检查人员,当然:-/ 在这些计算机上,每个数据库访问大约需要30秒!如果我安装了SQL Server Express 2008 R2,并

  • 我正在尝试通过url读取pdf。我遵循了许多stackoverflow建议并使用PyPdf2 FileReader从pdf中提取文本。我的代码如下所示: 我能够成功地提取第一个链接的文本。但是如果我对第二个pdf使用相同的程序。我没有收到任何短信。页码和文档信息似乎出现了。 我尝试通过终端从Pdfminer中提取文本,并能够从第二个pdf中提取文本。 你知道pdf有什么问题吗?或者我使用的库有缺点

  • 我在GitHub中使用这个项目:https://github.com/gankit0701/Face-Mask-Detection-In-android-App 这个使用TensorFlow Lite for mobile(Android)。它检测一个人是否戴面具。它在面部顶部绘制一个框(红色/绿色)。 我奇怪的问题是,如果我直接在设备上安装演示APK,面罩检测工作正常。但是当我在Android

  • 我们用FCM创建了一个带有通知的聊天应用程序,我的代码是正确的,我的设备也得到了推送通知数据,但一些中国制造的设备,如vivo、oppo、一加、小米,不允许通知显示,除非我在各自厂商的受保护的应用程序列表中添加应用程序。他们有没有办法解决这个问题。 https://hackernoon.com/notifications-in-android-are-horribly-broken-b8dbec6

  • 问题内容: alert(myVar1); return false; var myVar1; 上面的代码在IE,FF和Opera中引发错误,表明return语句必须包含在函数中。但它可以在Safari和Chrome中运行(显示)。 以上代码已在全局范围内编写。以外的所有功能。 任何原因? 问题答案: 在javaScript中,将变量移到脚本顶部,然后运行。所以当你运行它会做 这是因为javascr

  • 我一直在尝试使用Firebase认证来实现电话认证。它和一个Android模拟器一起工作。我在模拟器中使用了一个测试号和一个实数。两个作品。但是只有测试号可以在真实设备上工作,并对其他号抛出这个异常 我也在Firebase中设置了SHA密钥。