【问题标题】:What causes this error when consuming a web service?使用 Web 服务时出现此错误的原因是什么?
【发布时间】:2011-08-24 21:28:11
【问题描述】:

我有一个 Delphi XE 应用程序,它使用以 Cold Fusion 编写的 Web 服务(我无法控制服务的输出格式)。我使用 Delphi 中的 WSDL Importer 来创建我的单元以调用 Web 服务。我遇到了在 Delphi 中出现异常的情况,即“元素“数据”不包含单个文本节点”。

当我收到异常时,从 Web 服务返回的 XML 的相关部分是这样的:

<data soapenc:arrayType="xsd:anyType[][1]" xsi:type="soapenc:Array">
  <data soapenc:arrayType="xsd:anyType[2]" xsi:type="soapenc:Array">
    <data xsi:type="soapenc:string">6490</data>
    <data xsi:type="soapenc:string">Other Expense</data>
  </data>
</data>

如果来自 Web 服务的 XML 包含多个 &lt;data&gt; 子级,则不会发生异常。

<data soapenc:arrayType="xsd:anyType[][3]" xsi:type="soapenc:Array">
  <data soapenc:arrayType="xsd:anyType[2]" xsi:type="soapenc:Array">
    <data xsi:type="soapenc:string">2600</data>
    <data xsi:type="soapenc:string">Deferred Revenue</data>
  </data>
  <data soapenc:arrayType="xsd:anyType[2]" xsi:type="soapenc:Array">
    <data xsi:type="soapenc:string">4120</data>
    <data xsi:type="soapenc:string">Non-Credit Income</data>
  </data>
  <data soapenc:arrayType="xsd:anyType[2]" xsi:type="soapenc:Array">
    <data xsi:type="soapenc:string">6490</data>
    <data xsi:type="soapenc:string">Other Expense</data>
  </data>
</data>

是什么导致了这个异常,有没有办法在不改变网络服务本身的情况下解决它?

【问题讨论】:

  • 请贴delphi xml处理代码
  • 那是一些丑陋的 XML。没有真正命名的所有“数据”?有人不知道 XML 和 SOAP 是关于什么的!我的怀疑是 Delphi 代码无法处理所有同名的节点,所以会感到困惑,但见鬼!
  • @mj2008:我同意 XML 的丑陋。简化 Web 服务的编写是 Cold Fusion 的“特性”之一;开发人员不必考虑 XML 格式。如果他们是用 Delphi 编写的,它会干净得多。

标签: web-services delphi delphi-xe


【解决方案1】:

我不知道是什么导致了错误,但是是的,有办法解决它。您可以使用 RIO_AfterExecute() 处理程序修改 SOAPResponse,将 XML 更改为“使其适合”。这是一种丑陋的“大锤”方法,但它最终可以让您摆弄数据以解决各种问题。
查看您的两个示例,我将尝试使用 stringreplace 将 'xsd:anyType[][1]' 替换为 'xsd:anyType[][3]'。如果这不起作用,请尝试注入另一组具有空值的数据,以使其看起来不只是一个。

您需要一个 RIO 对象,然后将其连接到如下处理程序:

MyRIO.OnAfterExecute := self.RIO_AfterExecute;

在我的例子中,“self”指的是我围绕我的 SOAP 内容编写的一个类。

当您完成请求后,请务必将您的位置设置回 0。

这是一些未经测试的代码:

procedure MyWrapper.RIO_AfterExecute(const MethodName: string; SOAPResponse: TStream);
var 
  SL : TStringList;   
begin
  // do stuff with the SOAPResponse here. 
  // It's a stream, so I like to load it into a stringlist
  // ex: 
    SL := TStringList.Create;
    try
      SOAPResponse.Position := 0;
      SL.LoadFromSTream(SOAPREsponse);
      // fiddle with stringreplace here, to doctor up the SL.text.
      SOAPResponse.Position := 0;
      SOAPResponse.size := length(SL.Text);
      SL.SaveToStream(SOAPResponse);
    finally
      SL.free;
    end;
end;

【讨论】:

  • 我希望避免使用锤子方法,但我认为这是我目前唯一的选择。感谢您的建议。
【解决方案2】:

仅供参考,我今天遇到了同样的问题,经过几个小时的搜索,我找到了问题。事实是 WSDL 导入器错误地将某些类型映射到字符串,这导致 TXMLDocument 被指示读取文本节点而没有文本节点! 所以任何定义为字符串(或字符串数​​组)的类型都可能是错误的......

致 OP:检查导入单元中 soapenc:Array 类型的定义。

【讨论】:

    【解决方案3】:

    必须在您的 Delphi xml 读取代码中存在错误。它有时起作用的事实是偶然的。浏览 XML 的方式因您使用的组件而异。

    我相信这些会对你有所帮助

    Libraries and tutorials for XML in Delphi

    Where is a tutorial for using XML with Delphi?

    如果您发布 Delphi XML 处理代码,我们可以进一步研究。

    【讨论】:

    • 我没有编写任何代码来解析 XML。这一切都由 WSDL 导入器创建的单元处理。我对所有这些是如何工作的了解不多,只是我实例化了对象,调用了适当的方法,然后遍历了返回的变量数组。编写 Web 服务的 Cold Fusion 不会像我习惯的那样返回漂亮的对象。它映射到变体数组。
    • WSDL 导入器为 Web 服务调用创建此函数。function GetAdjustmentAccountList(const OperatingUnit: string; const RequestedDate: TXSDateTime): Map; stdcall; Map = array of mapItem; mapItem = class(TRemotable) private Fkey: Variant; Fvalue: Variant; published property key: Variant read Fkey write Fkey; property value: Variant read Fvalue write Fvalue; end;
    • 好吧,这就是 Cold Fusion 的工作原理……但也许您仍然可以从返回的变体之一中捕获该 xml 并自己处理它。您可能已经发现 WSDL Importer 导入 Cold Fusion 生成的 WSDL 的能力存在缺陷。这实际上是另一点,您是导入 WSDL 文件还是将 WSDL 导入器指向 Web 服务 URI?考虑为 Cold Fusion Web 服务编写一个 .Net 客户端并让 .Net 客户端充当 Delphi 应用程序的服务可能会让您感兴趣。然后你就可以控制暴露给 Delphi 的 web 服务接口了。
    猜你喜欢
    • 2019-05-01
    • 2017-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多