【问题标题】:Getting XML string from response stream using Indy's IDTCPClient使用 Indy 的 IDTCPClient 从响应流中获取 XML 字符串
【发布时间】:2013-11-21 16:08:08
【问题描述】:

我作为新员工接手的组件需要帮助,任务是向组件添加新方法。该组件使用 idTCPClient 而不是 idHTTP。原因请参见下面两个问题的回答。

Getting XML from response stream using Indy's IDTCPClient

Getting HTML from response stream using Indy's IDTCPClient

是的 Remy & Jerry,我已经开始重写组件以使用 tidHTTP 而不是 idTCPClient,但我是在自己的空闲时间而不是公司时间做的。所以,我需要今晚完成它。好的,回到这篇文章。

调用下面的get函数,得到如下响应

HTTP/1.0 200 OK 
Content-Length: 2069
Date: Wed, 20 Nov 2013 15:00:11 GMT
Content-Type: text/xml; charset=UTF-8


function TMyConnector.GET(aRawHeader: String): String;
begin

 if Not Connected then Connected := True;
if Connected then
  begin
  FRawRequest :=  'GET /'+ EncodeUrl(aRawHeader) + ' HTTP/'+HTTPVerText+#13#10+
                  'Host: '+FHost+#13#10+

                  'Cookie: UserHPos=IOGLO00003000090000C000BS; '+
                  'LOSID=qsBiy/wEDCq6tOXFzGbOlTD1lmo5AXdFnCkbzzPn6+qCeheYVyTcumRrjsqh+Hds4Fr2gZDazfDzGN1RA+nnHuQQeBy78ZUgctrZyyy9MnGl2qI/ulkV6EPxAfmmLg/lopRq99f5gAcG/dgtytAJjS+aD5DqtHGrAqjiqgtkwuA=; '+
                  'LoginHPos=IOGLO00003000090000C000BS; '+
                  'UIHPos=IOGLO00003000020000500003; '+
                  'LOSG=61939308-7C83-47ED-B909-2D2D10AD7026; '+
                  'fControllingBusiness=IOGLO000030000900001000050000200001'+#13#10+

                  'Connection: Close'+#13#10+
                  #13#10;

   FSock.Socket.Write(FRawRequest);
   FRawResponse := FSock.Socket.ReadLn(#13#10#13#10,nil);

  Result := FRawResponse;

  if ResponseStream = nil then ResponseStream := TMemoryStream.Create
   else ResponseStream.SetSize(0);

   FSock.Socket.ReadStream(ResponseStream,-1,True);

  if Connected and (Not KeepAlive) then Connected := False;
  end;
end;

我现在如何从 responseStream 中获取 XML 字符串?

现有的 JSON 方法已经存在

Procedure TMyConnector.GenerateJSON;
begin
if ResponseStream <> nil then
  Begin
   ResponseJSON_V := TJSONObject.ParseJSONValue(StreamToArray(ResponseStream),0) as TJSONValue;
  End;
end;

我想为 XML 创建一个方法,我尝试了以下方法:

Procedure TMyConnector.GenerateXML;
var
 S: String;
begin
if ResponseStream <> nil then
   // code here to convert ResponseStream to XML
    ResponseXML_v:= S;
end;

【问题讨论】:

  • 有效吗?有什么问题?
  • 将所有接收到的数据保存到TXT文件中。然后在一些十六进制+文本编辑器中打开它并进行调查。另请阅读 Wikipedia 和 HTTP RFC 上的 HTTP 协议描述。返回的数据有望由行组成,由 value==10 个字节(或 13、10 个序列)分隔。最初会有“标题”- 7 位 (0..127) AnsiChar 中的响应描述。会有诸如数据长度、数据字符集/代码页之类的标头以及将该数据转换为 7 位字节的方法(例如 ZIP+base64 或任何其他转换组合)。然后会有一个空行和数据,整个或块。
  • 这是您正在与之通信的网络服务吗?那么这不是要走的路......
  • 所以你必须解析 HTTP 标头,读取数据到最后,如果它被分块为多个数据包,然后构建转换图并将数据反转换为原生 Delphi 字符串.或者你可以使用任何现成的 HTTP 组件:stackoverflow.com/questions/19653886
  • HTTP 不是一个“流”——它是一个非常复杂的野兽,发展了大约 20 年,并且一次又一次地扩展。因此,您要么使用 HTTP,要么使用普通的非 HTTP 二进制流。在后一种情况下,您必须以一种或另一种方式解决几个问题:哪个字符集用于 string ?字符串有多长?流何时结束或尚未结束?因此,如果您不使用 HTTP 服务器 - 那么您将不得不设计和实施一些其他协议,修复或协商这些问题。

标签: xml delphi indy


【解决方案1】:

我现在如何从 responseStream 中获取 XML 字符串?

好吧,至少你必须从 responseStream 中读取一些数据。 但是您没有在 GenerateXML 代码中阅读它。

.

Procedure TMyConnector.GenerateXML;
var
 S: String; SS: TStringStream;
begin
 if ResponseStream <> nil then begin
   S := '';
   SS := TStringStream.Create(S, TEncoding.UTF8, False);
   try
     SS.LoadFromStream(ResponseStream);
     // original: ResponseXML_v := S;   - so ResponseXML_v is jsut a string....
     ResponseXML_v:= SS.DataString; 
   finally
     SS.Destroy;
   end;
 end;
end;

但这里 UTF8 纯粹是猜测,如果 ResponseStream 是例如 zip 存档或 base64 编码文本或其他内容,它将失败。

获得字符串后,您必须对其进行解析。但是解析字符串并通过网络传输是不同的问题。 您可以在此处获取 XML 解析器列表:What is the fastest XML Parser available for Delphi?

我还建议您尝试使用 SuperObject 库。我不确定它是否支持所有 XML 功能,但它具有 JSON 和 XML 的通用 API,这应该是件好事。

不管怎样,你可以通过 StackOverflow 谷歌一下,看看 Delphi 库存的 DB-Express JSON 解析器有许多错误和限制。

https://code.google.com/p/superobject/source/browse/superxmlparser.pas

Procedure TMyConnector.GenerateXML;
begin
 Response_v := nil;
 if ResponseStream <> nil then begin
    ResponseStream.Position := 0;
    Response_v:= XMLParseStream(ResponseStream)
 end;
end;

Procedure TMyConnector.GenerateJSON;
begin
 Response_v := nil;
 if ResponseStream <> nil then begin
    ResponseStream.Position := 0;
    Response_v:= TSuperObject.ParseStream(ResponseStream);
 end;
end;

https://superobject.googlecode.com/git/readme.html

但是,在解析之前,字符集检测和转换可能仍然是您的责任。


因此,“分离关注点”并使用“DRY 原则”(其中的“代码重用”部分),我们将函数统一为类似的东西:

{$ScopedEnums On}
type ResponseFormat = (XML, JSON)

function ParseResponse(const ResponseStream: TStream; 
           const Format: ResponseFormat): iSuperObject;
begin
 Result := nil;
 if ResponseStream = nil then exit;
 if ResponseStream.Size <= 0 then exit;

 ResponseStream.Position := 0;
 case Format of
   ResponseFormat.XML:  Result := XMLParseStream(ResponseStream);
   ResponseFormat.JSON: Result := TSuperObject.ParseStream(ResponseStream);
   else raise Exception.CreateFmt(
          'Unknown response format #%d, cannot parse it.',  [ Ord(ResponseFormat) ] );
 end;
end;

现在您的责任是为 JSON 或 XML 响应统一调用该函数,并始终为其提供正确的输入数据流。


总的来说,你有两个问题摆在你面前:

  1. 如何从 HTTP 服务器获取文本文件并将其字符集转换为原生 Delphi 字符串字符集
  2. 如何将包含 XML 或 JSON 的 Delphi 字符串解析为易于使用的 OO-API。

这些是不同的问题,您应该将您的情境和程序解构为这些部分。否则,你会把这些东西纠缠到没人能理解(包括你)和调试的狂野混合物中。你应该解耦和解开,而不是相反。

您告诉服务器您收到了相同的请求,但是 GenerateJSON 函数获得了流而 GenerateXML 没有?应该是什么意思?只是代码中存在一些您没有显示的关键差异。这意味着您不了解您的代码的哪些部分在做什么、何时以及为什么。你应该“分而治之”。将您的程序分成几部分。一件将从服务器获取数据流 - 仅此而已。另一部分将解析流-仅此而已。然后你应该单独调试它们(并提出单独的问题)。当您有 nil 流时,没有必要调试和重做解析。所以......单独的关注点。制作函数,获取流,并确保它确实有效。例如,通过将流保存到文本文件并使用 Text+Hex 编辑器检查其内容。完成第一项任务后 - 转到下一项,解析接收到的流。但只有在你确保你总是能得到信息流之后。

【讨论】:

  • HTTP 中的换行符应始终为#13#10,无论平台如何。我认为 OP 更多地停留在如何读取内容字节(到字符串中),并假设其余部分会自动工作。
  • @LIVESTUFF 因为您要求比较 GetJSON 的实现 - 确实读过它 - 和 GetXML 的实现 - 没有(在你编辑问题并完全删除实现之前)。然后你想知道,为什么前者有效而后者无效。 nil 怎么样 - 然后显示创建流的相同代码,用数据填充它,然后统一调用 GetXML 或 GetJSON。如果在您的情况下流为零 - 这仅意味着您省略的代码有所不同,即实际执行调用的代码。
  • @LIVESTUFF 添加统一调用 GenerateXML 或 GenerateJSON 的代码,以查看您实际上为它们创建了相同的流。您最初的实现没有读取 ResponseStream 不是因为它是 nil - 它无论如何都不会读取它,只是没有一次尝试在您的初始源中读取它
  • @LIVESTUFF 那么我建议您从阅读总体指南开始:en.wikipedia.org/wiki/Divide_and_conquer_algorithmcatb.org/esr/faqs/smart-questions.html - 掌握这将有助于您更好地学习。
  • 现在,您的问题绝对不清楚和含糊。你有什么输入?没有答案。好吧,我们可能会阅读您的代码并尝试猜测您有 ResponseStream:TStream 输入 - 但我们为什么要猜测?你应该给我们所有的细节来完全理解这个问题。并使问题成为一个狭窄的单一问题案例。那么你想要什么结果呢?您最初的代码说“XML”实际上是指“字符串”。这看起来很疯狂,但这就是你所说的。
猜你喜欢
  • 2013-12-02
  • 2013-12-01
  • 1970-01-01
  • 2014-08-30
  • 1970-01-01
  • 2011-03-01
  • 1970-01-01
  • 2021-04-16
  • 1970-01-01
相关资源
最近更新 更多