views:

299

answers:

2

I am trying to use Java based webservice and have soap request:

<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"&gt;
  <SOAP-ENV:Body xmlns:NS1="http://something/"&gt;
    <NS1:getRequest id="1">
      <sessionId xsi:type="xsd:string"></sessionId>
      <reportType xsi:type="NS1:reportType">ALL</reportType>
      <xsd:dateFrom xsi:type="xsd:dateTime">2010-05-30T23:29:43.088+02:00</xsd:dateFrom>
      <xsd:dateTo xsi:type="xsd:dateTime">2010-05-31T23:29:43.728+02:00</xsd:dateTo>
    </NS1:getRequest>
    <parameters href="#1" />
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

It doesn't work, because webservice doesn't recognize dates as parameters. When I change

      <xsd:dateFrom xsi:type="xsd:dateTime">2010-05-30T23:29:43.088+02:00</xsd:dateFrom>
      <xsd:dateTo xsi:type="xsd:dateTime">2010-05-31T23:29:43.728+02:00</xsd:dateTo>

to

      <dateFrom xsi:type="xsd:dateTime">2010-05-30T23:29:43.088+02:00</xsd:dateFrom>
      <dateTo xsi:type="xsd:dateTime">2010-05-31T23:29:43.728+02:00</xsd:dateTo>

everything works ok, but Delphi (without Delphi source code changes) doesn't allow to change generated XML, it has only some options. Is it possible to set conversion options, so TSXDateTime is converted to <dateFrom, not <xsd:dateFrom tag? Did you meet that problem?

A: 

You can intercept the XML prior to deserialization and edit to your heart's content, with stringreplace. You'll need to hook into one of the RIO events.

Update: Hook into it here: HTTPRIO1AfterExecute(const MethodName: string; SOAPResponse: TStream);

The SOAPResponse is a stream, not quite as easy to fiddle with as a string, but it definitely IS modifyable. I can post some example code tomorrow afternoon.

Edit: OnAfterExecute is present in D2007 and later, and you can use it in D2005 by using the D2007 SOAP sources. Not sure about D7! You may be SOL.

Edit: On D7, you could probably hack the code to provide your own OnAfterExecute event. i.e. modify rio.pas to include the handler for you. Also, a common mistake when working with the stream object is failure to reset the position to 0 when you're done.

Edit: you can also edit the request in the BeforeExecute, although possibly not with the Delphi7 code. In D2010 (which I have in front of me now), the SOAPRequest is a stream. In D2007 (which I've worked with extensively, but actually using the D2007 code on D2005), I believe it's a string. In my D2005/2007 project, we're extensively editing the request with a series of StringReplace() statements in the OnBeforeExecute.

Chris Thornton
Where? It is not possible. I wrote about it in question. THTTPRIO has OnBeforeExecute event and it even has parameter with xml declared as var, but it is still not changeable. If you look at Delphi sources, there is even note about it.
LukLed
Sorry, but OnAfterExecute happens after method is executed, so there is nothing to change. OnAfterExecute contains xml of response and I want to change request.
LukLed
@LukLed - sorry about that. See my last addition, for using the BeforeExecute to edit the XML. This may not work in D7, but is available in D2005 and later. You may be able to modify the source to allow editing of that string/stream.
Chris Thornton
@Chris Thornton: OnBeforeExecute doesn't allow to change xml in D7. I can change Delphi source code, but I don't want to do that. I am looking for other solution, maybe a setting of THTTPRIO.
LukLed
A: 

I found solution. I inherited from THttpRIO and because DoBeforeExecute is virtual, I changed its implementation (DoBeforeExecute is taken from Delphi 2007, which allows to change xml in OnBeforeExecute). Then I changed my automatically generated from WSDL unit to use TMyHttpRIO:

unit MyHttpRIO;

interface

uses
  RIO, Classes, SOAPHTTPClient;

type
  TMyHttpRIO = class(THttpRIO)
    procedure DoBeforeExecute(const MethodName: string; Request: TStream); override;
  private
  end;

implementation

{ TMyHttpRIO }

procedure TMyHttpRIO.DoBeforeExecute(const MethodName: string;
  Request: TStream);
var
  StrStrm: TStringStream;
  SavedRequest: WideString;
  ReqWideStr: WideString;
begin
  if Assigned(OnBeforeExecute) then
  begin
    { Ideally we would change the signature of this event to take a Stream.
      The change to stream was necessary for attachment and encoding support.
      And it makes the event consistent.... However, for the sake of
      backward compatibility.... }
    StrStrm := TStringStream.Create('');
    try
      StrStrm.CopyFrom(Request, 0);
      Request.Position := 0;
      ReqWideStr := UTF8Decode(StrStrm.DataString);
      SavedRequest := ReqWideStr;
      OnBeforeExecute(MethodName, ReqWideStr);
    finally
      StrStrm.Free;
    end;
    if (Length(SavedRequest) <> Length(ReqWideStr)) or (SavedRequest <> ReqWideStr) then
    begin
      // Copy changes made to ReqWideStr in the event back to the Request stream
      StrStrm := TStringStream.Create(string(ReqWideStr));
      try
        StrStrm.Position := 0;
        Request.Size := 0;
        Request.CopyFrom(StrStrm, 0);
        Request.Position := 0;
      finally
        StrStrm.Free;
      end;
    end;
  end;
end;

end.
LukLed