Acorn
Acorn

Reputation: 1165

Cannot get string from StringStream

I am sending an HTTP Get request to Google's Map API, and I fill my StringStream with the response. However, when I try to read from the stream, I am just presented with an empty string ''.

{ Attempts to get JSON back from Google's Directions API }
function GetJSONString_OrDie(url : string) : string;
var
  lHTTP: TIdHTTP;
  SSL: TIdSSLIOHandlerSocketOpenSSL;
  Buffer: TStringStream;
begin
  {Sets up SSL}
  SSL := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
  {Creates an HTTP request}
  lHTTP := TIdHTTP.Create(nil);
  {Sets the HTTP request to use SSL}
  lHTTP.IOHandler := SSL;

  {Set up the buffer}
  Buffer := TStringStream.Create(Result);
  {Attempts to get JSON back from Google's Directions API}
  lHTTP.Get(url, Buffer);

  Result:= Buffer.ReadString(Buffer.Size); //An empty string is put into Result


  finally
    {Frees up the HTTP object}
    lHTTP.Free;
    {Frees up the SSL object}
    SSL.Free;

end;

Why am I getting an empty string back, when I can see that the StringStream Buffer has plenty of data (size of 32495 after the Get is called).

I've tested my call, and I am returned with valid JSON.

Upvotes: 1

Views: 2742

Answers (2)

Remy Lebeau
Remy Lebeau

Reputation: 595392

First, you are using TStringStream to receive the response data. If you are using Delphi 2009+, DO NOT do that! TStringStream is tied to a specific encoding that has to be declared in the constructor before the stream is populated with data, and it cannot be changed dynamically. The default encoding is TEncoding.Default, which represents the OS default encoding. If the HTTP response uses a different encoding, the data will not decode to a String correctly.

Second, you are not seeking the stream's Position back to 0 before calling ReadString(). An easier way to retrieve a TStringStream's content as a decoded String is to use the DataString property instead, which ignores the Position property and returns the entire stream content as a whole:

Result := Buffer.DataString;

Third, you are doing too much manual work. TIdHTTP.Get() has an overloaded version that returns a decoded String. The benefit of using this method is that it uses the actual charset of the response, rather than the charset of a TStringStream:

function GetJSONString_OrDie(const URL: string): string;
var
  lHTTP: TIdHTTP;
  SSL: TIdSSLIOHandlerSocketOpenSSL;
begin
  {Creates an HTTP request}
  lHTTP := TIdHTTP.Create(nil);
  try
    {Sets the HTTP request to use SSL}
    lHTTP.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(lHTTP);    
    {Attempts to get JSON back from Google's Directions API}
    Result := lHTTP.Get(URL);
  finally
    {Frees up the HTTP object}
    lHTTP.Free;
  end;
end;

Which can be simplified further if you are using an up-to-date version of Indy (see this blog post for details):

function GetJSONString_OrDie(const URL: string): string;
var
  lHTTP: TIdHTTP;
begin
  {Creates an HTTP request}
  lHTTP := TIdHTTP.Create(nil);
  try
    {Attempts to get JSON back from Google's Directions API}
    Result := lHTTP.Get(URL);
  finally
    {Frees up the HTTP object}
    lHTTP.Free;
  end;
end;

Upvotes: 5

Birger
Birger

Reputation: 4353

Maybe first set Buffer.Position := 0?

Upvotes: 3

Related Questions