【发布时间】:2020-01-09 22:55:33
【问题描述】:
我正在尝试使用 OLE 变体在 Embarcadero C++Builder 10.1 Berlin 中进行 XML 验证。我的最终目标是显示所有验证错误,而不仅仅是第一个错误(在this MSDN example 之后)。我的课在下面。当我运行以下行时,出现“属性名称无效”的异常。
FXmlDomDoc.Exec( XmlFuncSetProperty );
如果我将此行注释掉,一切都会正常运行。
这使得“MultipleErrorMessages”似乎不是 MSXML2.DOMDocument.6.0 上的 setProperty() 的有效参数。但是,当我查看 the list of Second-Level DOM Properties 时,似乎这是 6.0 XML DOM 对象的有效二级属性。
我尝试过的:
将 XmlFuncSetProperty 定义为过程而不是函数;同样的错误。
将 ValidateOnLoad / ValidateOnParse 设置为 false,以防万一它们以某种方式影响了这一点;同样的错误。
使用 _di_IXMLDOMDocument3、_di_IXMLDOMSchemaCollection2、_di_IXMLDOMParseError 重写类。我在这些类中找不到对多个错误的任何支持。我确实在其他几个类中找到了我需要的函数,但它们是纯虚拟的。
问题:
我在这里缺少什么?为什么会出现这个错误?
在 C++ Builder 中是否有更好的方法来执行此操作?
.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( XmlFuncSetProperty, "setProperty" ),
RegisterAutoCmd( XmlSetValidateOnParse, "validateOnParse" ),
RegisterAutoCmd( XmlSetResolveExternals, "resolveExternals" ),
RegisterAutoCmd( XmlSetSchemas, "schemas" ),
RegisterAutoCmd( XmlGetParseError, "parseError" ),
RegisterAutoCmd( ParseErrorGetReason, "reason" )
{
if ( _SchemaLocation.IsEmpty() )
{
FInvalidMsg = "No Schema Location Specified";
}
else if ( ! FileExists( _SchemaLocation ) )
{
FInvalidMsg = "Schema File Does Not Exist: " + _SchemaLocation;
}
else
{
FInvalidMsg = "";
}
if ( FInvalidMsg.Length() > 0 )
{
return;
}
// Instantiate the OLE objects
FSchemaCache = CreateOleObject( "MSXML2.XMLSchemaCache.6.0" );
FXmlDomDoc = CreateOleObject( "MSXML2.DOMDocument.6.0" );
// Set static args that shouldn't change
AutoCmdArg( CacheSetValidateOnLoad, true );
AutoCmdArg( XmlSetValidateOnParse, true );
AutoCmdArg( XmlSetResolveExternals, true );
AutoCmdArgs( XmlFuncSetProperty, "MultipleErrorMessages", 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 );
// Set up Xml Dom doc as much as we can...
FXmlDomDoc.Exec( XmlSetValidateOnParse );
FXmlDomDoc.Exec( XmlSetResolveExternals );
FXmlDomDoc.Exec( XmlSetSchemas );
FXmlDomDoc.Exec( XmlFuncSetProperty );
}
//------------------------------------------------------------------------------
String __fastcall TXmlValidator::ValidationError( String _Xml )
{
if ( FInvalidMsg.Length() > 0 )
{
return FInvalidMsg;
}
AutoCmdArg( XmlProcLoadXml, AnsiString( _Xml ) ); // update the XML for re-parsing
FXmlDomDoc.Exec( XmlProcLoadXml ); // Load the doc from the XML
Variant ParseErr = FXmlDomDoc.Exec( XmlGetParseError ); // Get the parseError object
return ParseErr.Exec( ParseErrorGetReason ); // Extract the reason
}
//------------------------------------------------------------------------------
.h 文件:
//------------------------------------------------------------------------------
#ifndef XmlValidatorUH
#define XmlValidatorUH
//------------------------------------------------------------------------------
class PACKAGE TXmlValidator
{
private:
String FInvalidMsg;
// OLE Variant Variables
Variant FSchemaCache;
Variant FXmlDomDoc;
// TAutoCmd Variables
Procedure CacheProcAdd;
PropertySet CacheSetValidateOnLoad;
Procedure XmlProcLoadXml;
Function XmlFuncSetProperty;
PropertySet XmlSetValidateOnParse;
PropertySet XmlSetResolveExternals;
PropertySet XmlSetSchemas;
PropertyGet XmlGetParseError;
PropertyGet ParseErrorGetReason;
public:
__fastcall TXmlValidator( String _SchemaLocation );
String __fastcall ValidationError( String _Xml );
};
//------------------------------------------------------------------------------
#endif
【问题讨论】:
-
我不知道为什么它说
MultipleErrorMessages不是有效属性。这意味着您将MultipleErrorMessages设置为 OLE 对象本身的属性,而不是调用setProperty()并将"MultipleErrorMessages"作为参数传递。您的代码建议后者。更糟糕的情况是,您可能需要使用变体的OleProcedure()方法来调用setProperty,例如:FXmlDomDoc.OleProcedure(_D("setProperty"), WideString(L"MultipleErrorMessages"), true);此外,您应该避免在OLE 代码中使用char*字符串,它不能很好地工作。请改用wchar_t*或WideString -
非常感谢,@RemyLebeau!再一次,你救了我的培根。我真的很感谢你的辛勤工作和慷慨。你有 PayPal 或 Patreon 什么的吗? :-)
标签: c++builder domdocument vcl ole