使用Indy的IDTCPClient从响应流中获取XML字符串

时间:2013-11-20 15:17:53

标签: xml delphi indy

我需要帮助我作为新员工接管的组件,其任务是向组件添加新方法。该组件使用idTCPClient而不是idHTTP。请参阅以下两个问题中的答案,了解原因。

Getting XML from response stream using Indy's IDTCPClient

Getting HTML from response stream using Indy's IDTCPClient

是的Remy&杰里,我已经开始重写组件以利用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;

1 个答案:

答案 0 :(得分: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

然而,在解析之前,charset检测和转换可能仍然是您的责任。


因此,“分离关注点”并使用“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服务器获取文本文件并将其charset转换为本机Delphi字符串charset
  2. 如何将包含XML或JSON的Delphi字符串解析为易于使用的OO-API。
  3. 这些是不同的问题,你应该将你的情况和你的程序解构为这些部分。否则你会陷入无法理解(包括你)和调试的狂野混音。你应该去耦合和解缠结,而不是相反。

    您告诉您向服务器发出相同的请求,但GenerateJSON函数获取流而GenerateXML没有?它应该是什么意思?只是代码中有一些重要的区别,你没有显示。这意味着你不要理解你的代码在做什么,何时以及为什么做什么。你应该“分而治之”。将程序分成几部分。一件将从服务器获取数据流 - 没有别的。另一部分将解析流 - 而不是其他任何东西。然后你应该单独调试它们(并提出单独的问题)。当你有nil流时,没有必要调试和重做解析。所以...单独关注。制作功能,支持流,并确保它能够正常工作。例如,将流保存到文本文件并使用Text + Hex编辑器检查其内容。完成第一个任务后 - 转到下一个任务,然后解析收到的流。但只有在你确定你总是得到了流之后。