gpt4 book ai didi

delphi - 如何在 Delphi 中停止使用 IVBSAXXMLReader 解析 XML 文档?

转载 作者:行者123 更新时间:2023-12-03 15:08:52 25 4
gpt4 key购买 nike

为了在Delphi(2007)程序中快速解析一些大型XML文档,我实现了IVBSAXContentHandler接口(interface)并像这样使用它:

FXMLReader := CoSAXXMLReader60.Create;
FXMLReader.contentHandler := Self;
FXMLReader.parseURL(FXmlFile);

只要我简单地解析整个文件,这就可以正常工作,但是一旦找到我正在寻找的内容,我就想停止。因此,我的 IVBSAXContentHandler.startElement 实现会检查某些条件,当条件成立时应中止进一步解析。我试过这个:

procedure TContentHandler.startElement(var strNamespaceURI, strLocalName,  strQName: WideString; const oAttributes: IVBSAXAttributes);
begin
if SomeCondition then
SysUtils.Abort;
end;

不幸的是,这会引发相当无用的 EOleException“灾难性失败”。 (我也尝试引发自定义异常,结果相同。)

MSDN说如下:

The ErrorHandler interface essentially allows the XMLReader to signal the ContentHandler implementation that it wants to abort processing. Conversely, ContentHandler implementations can indicate to the XMLReader that it wants to abort processing. This can be accomplished by simply raising an application-specific exception. This is especially useful for aborting processing once the implementation finds what it is looking for:

Private Sub IVBSAXContentHandler_characters(ByVal strChars As String)
' I found what I was looking for, abort processing
Err.Raise vbObjectError + errDone, "startElement", _
"I got what I want, let's go play!"
End Sub

所以,显然我还需要以某种方式实现 IVBSAXErrorHandler 接口(interface)。该接口(interface)需要三个方法:

procedure TContentHandler.error(const oLocator: IVBSAXLocator;
var strErrorMessage: WideString; nErrorCode: Integer);
begin

end;

procedure TContentHandler.fatalError(const oLocator: IVBSAXLocator;
var strErrorMessage: WideString; nErrorCode: Integer);
begin

end;

procedure TContentHandler.ignorableWarning(const oLocator: IVBSAXLocator;
var strErrorMessage: WideString; nErrorCode: Integer);
begin

end;

并且还必须在调用 ParseURL 方法之前进行赋值:

FXMLReader := CoSAXXMLReader60.Create;
FXMLReader.contentHandler := Self;
FXMLReader.errorHandler := Self;
FXMLReader.parseURL(FXmlFile);

不幸的是,这没有任何区别,因为现在 fatalError 处理程序是通过 strErrorMessage = '灾难性失败' 调用的。如果方法体为空,这仍然会导致上面提到的无用的 EOleException“灾难性失败”。

所以,现在我没有主意了:

  • 我需要在错误处理程序接口(interface)中实现一些特殊的东西吗?
  • 我是否需要引发特定异常而不是 EAbort?
  • 还是我还漏掉了其他东西?
<小时/>

编辑:

根据Ondrej Kelle的回答,这是我最终使用的解决方案:

声明以下常量:

const
// idea taken from Delphi 10.1 unit System.Win.ComObj:
EExceptionRaisedHRESULT = HResult(E_UNEXPECTED or (1 shl 29)); // turn on customer bit

向 TContentHandler 类添加两个新字段:

FExceptObject: TObject;
FExceptAddr: Pointer;

将此代码添加到析构函数:

FreeAndNil(FExceptObject);

添加一个新方法 SafeCallException:

function TContentHandler.SafeCallException(ExceptObject: TObject; ExceptAddr: Pointer): HResult;
var
GUID: TGUID;
exc: Exception;
begin
if ExceptObject is Exception then begin
exc := Exception(ExceptObject);
// Create a copy of the exception object and store it in the FExceptObject field
FExceptObject := exc.NewInstance;
Exception(FExceptObject).Create(exc.Message);
Exception(FExceptObject).HelpContext := exc.HelpContext;
// Store the exception address in the FExceptAddr field
FExceptAddr := ExceptAddr;
// return a custom HRESULT
Result := EExceptionRaisedHRESULT;
end else begin
ZeroMemory(@GUID, SizeOf(GUID));
Result := HandleSafeCallException(ExceptObject, ExceptAddr, GUID, '', '');
end;
end;

向调用代码添加异常处理程序:

var
exc: Exception;
begin
try
FXMLReader := CoSAXXMLReader60.Create;
FXMLReader.contentHandler := Self;
// we do not need an errorHandler
FXMLReader.parseURL(FXmlFile);
FXMLReader := nil;
except
on e: EOleException do begin
// Check for the custom HRESULT
if e.ErrorCode = EExceptionRaisedHRESULT then begin
// Check that the exception object is assigned
if Assigned(FExceptObject) then begin
exc := Exception(FExceptObject);
// set the pointer to NIL
FExceptObject := nil;
// raise the exception a the given address
raise exc at FExceptAddr;
end;
end;
// fallback: raise the original exception
raise;
end;
end;
end;

虽然这对我有用,但它有一个严重的缺陷:它仅复制原始异常的 Message 和 HelpContext 属性。因此,如果有更多属性/字段,例如

EInOutError = class(Exception)
public
ErrorCode: Integer;
end;

当调用代码中重新引发异常时,这些不会被初始化。

优点是您将在调试器中获得正确的异常地址。请注意,您将无法获得正确的调用堆栈。

最佳答案

只需调用Abort;就可以了。在这种情况下,只需覆盖 SafeCallException在您的 IVBSAXContentHandler 实现类中:

function TContentHandler.SafeCallException(ExceptObject: TObject; ExceptAddr: Pointer): HRESULT;
begin
Result := HandleSafeCallException(ExceptObject, ExceptAddr, TGUID.Empty, '', '');
end;

HandleSafeCallException ComObj 中提供的值将导致您提出的 EAbort 转换为 HRESULTE_ABORT,然后该值将被转换为 HRESULTE_ABORTSafeCallError 翻译回 EAbort

或者,您可以引发自己的异常类,覆盖 SafeCallException 将其转换为您的特定 HRESULT 值,并将 SafeCallErrorProc 替换为您自己的将其转换回 Delphi 异常,然后您可以在调用方处理该异常。

关于delphi - 如何在 Delphi 中停止使用 IVBSAXXMLReader 解析 XML 文档?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38992143/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com