7

I have a SOAP application created with delphi.

Input comes to server correct. But output is always empty. r object (response) created, but length(r.notes) always 0. If I do application without arrays, it also works correct. Where is the problem? 3 days of googling and trying different combinatios did not help.

Interface:

////////////// INPUT ///////////////////////
type TClientInformationStructure= class(TRemotable)
  private
    fClientApplicationName:string;
    fClientApplicationPassword:string;
    fRequestIdentifier:string;
    fStartSequenceNumber:integer;
    fNumberOfNotes:integer;
  published
    property ClientApplicationName:string read fClientApplicationName  write fClientApplicationName;   //Name of calling application
    property ClientApplicationPassword:string read fClientApplicationPassword write fClientApplicationPassword;          //Password that calling application must use to call the service
    property RequestIdentifier:string read fRequestIdentifier write fRequestIdentifier;//Transaktionsid from calling system that is stamped in all loggings for service,
                                                                                       //so that later it is easy to compare client and server logs. May be null.
    property StartSequenceNumber:integer read fStartSequenceNumber write fStartSequenceNumber;
    property NumberOfNotes:integer read fNumberOfNotes write fNumberOfNotes;
end;

///////////// OUTPUT ////////////////////////////
Type TNote=class(tremotable)
  private
    fNotetId:string;
    fSequenceNumber:integer;
    fDeleteMark:boolean;
    fAuthorRole:string;
    fAuthorUserName:string;
    fAuthor:string;
    fAcceptTime:tdateTime;
    fOrganizationalUnit:string;
    fLocationStartTime:tdateTime;
    fLocationEndTime:TdateTime;
    fBeadWard:string;
    fPersonCivilRegistrationIdentifier:string;
    fNoteType:string;
    fNoteText:string;
    fMoreNotesAvailable:boolean;
  public
    property NotetId:string read fNotetId  write fNotetId;
    property SequenceNumber:integer read fSequenceNumber write fSequenceNumber;
    property DeleteMark:boolean read fDeleteMark write fDeleteMark;
    property AuthorRole:string read fAuthorRole write fAuthorRole;
    property AuthorUserName:string read fAuthorUserName write fAuthorUserName;
    property Author:string read fAuthor write fAuthor;
    property AcceptTime:tdateTime read fAcceptTime write fAcceptTime;
    property OrganizationalUnit:string read fOrganizationalUnit write fOrganizationalUnit;
    property LocationStartTime:tdateTime read fLocationStartTime write fLocationStartTime;
    property LocationEndTime:TdateTime read fLocationEndTime write fLocationEndTime;
    property BeadWard:string read fBeadWard write fBeadWard;
    property PersonCivilRegistrationIdentifier:string read fPersonCivilRegistrationIdentifier write fPersonCivilRegistrationIdentifier;
    property NoteType:string read fNoteType write fNoteType;
    property NoteText:string read fNoteText write fNoteText;
    property MoreNotesAvailable:boolean read fMoreNotesAvailable write fMoreNotesAvailable;
end;

type TnoteStructure = array of TNote;

type tNoteCollection=class(tremotable)
  private
    fnotes:TnoteStructure;
  public
    property notes:TnoteStructure read fnotes write fnotes;
end;

type
  ibla = interface(IInvokable)
   ['{FFD831EC-56B1-4C0E-9CCE-8D9C7ECEE656}']
    function GetNotes(ClientInformationStructure:TClientInformationStructure)
              : tNoteCollection; stdcall;
  end;

implementation

initialization
  RemClassRegistry.RegisterXSClass(TClientInformationStructure);
  RemClassRegistry.RegisterXSClass(Tnote);
  RemClassRegistry.RegisterXSClass(tNoteCollection);
  RemClassRegistry.RegisterXSInfo(TypeInfo(TnoteStructure));
  InvRegistry.RegisterInterface(TypeInfo(ibla));

finalization
  RemClassRegistry.UnRegisterXSClass(TClientInformationStructure);
  RemClassRegistry.unRegisterXSClass(Tnote);
  RemClassRegistry.unRegisterXSClass(tNoteCollection);
  RemClassRegistry.unRegisterXSInfo(TypeInfo(TnoteStructure));
  InvRegistry.UnRegisterInterface(TypeInfo(ibla));
end.

Implementation:

type
  Tbla = class(TInvokableClass, ibla)
  public
    function GetNotes(ClientInformationStructure:TClientInformationStructure)
              : TNotecollection; stdcall;
  end;

implementation

function Tbla.GetNotes(ClientInformationStructure:TClientInformationStructure)
              : TNotecollection;
var n:tNoteStructure;
begin
  try
    result:=TNotecollection.Create;
    setlength(n,1);
    n[0]:=tnote.create;
    n[0].NotetId:=inttostr(random(100));
    n[0].AuthorUserName:='!1!'+ClientInformationStructure.ClientApplicationName;
    n[0].SequenceNumber:=999;
    result.notes:=copy(n);
  except
    on e:exception do addtolog(e.Message)
  end;
end;

initialization
  InvRegistry.RegisterInvokableClass(Tbla)

finalization
  InvRegistry.unRegisterInvokableClass(Tbla)

Client side:

  c:=tclientinformationstructure.Create;
  try
    c.ClientApplicationName:=labelededit1.Text;
    c.ClientApplicationPassword:=labelededit2.Text;
    c.RequestIdentifier:=labelededit3.Text;
    c.StartSequenceNumber:=strtointdef(labelededit4.Text,0);
    c.NumberOfNotes:=strtointdef(labelededit5.Text,0);
    r:=(HTTPRIO1 as ibla).GetNotes(c);
    if assigned(r) then
      if length(r.notes)>0 then
        if assigned(r.notes[0]) then showmessage(r.notes[0].AuthorUserName);
  finally
    freeandnil(c);
    if assigned (r.notes[0]) then freeandnil(r.notes[0]);
    if assigned(r) then freeandnil(r);
  end;
Taras
  • 391
  • 4
  • 10
  • 1
    Try `RegisterSerializeOptions` as described in this [answer](http://stackoverflow.com/questions/5089633/array-of-int-in-soap-client/5092806#5092806). – Ondrej Kelle Aug 11 '11 at 11:59
  • TOndrej: done, did not help. Tried to make an array type not a property but a function result. In this case client got array with right length, with created objects, but all object's fields were empty or 0. – Taras Aug 12 '11 at 08:24
  • And response is like this: ` ` – Taras Aug 12 '11 at 08:30

3 Answers3

4

In the Server, to go to the SOAP web module, select the HTTPSoapPascalInvoker component, and open up the Options property in the Object Inspector. Make sure the option "soRootRefNodesToBody" is checked. That will ensure that in the SOAP resonse, the elements are passed outside of the response node, in the body, where the client can find them.

Groetjes, Bob Swart

Bob Swart
  • 1,278
  • 7
  • 6
4

Thanks to all. All works fine with this code:

Server

Interface part:

interface

const
  IS_OPTN = $0001;
  IS_UNBD = $0002;

type tNoteCollection=class(tremotable)
  private
    fnotes:TnoteStructure;
    procedure Setnotes(Index: Integer; const anotes: TnoteStructure);
  published
    property notes:TnoteStructure index(IS_OPTN or IS_UNBD)  read fnotes write setnotes;
    procedure setlen(count:byte);
    function getlen:integer;
end;

implementation

procedure tNoteCollection.setlen(count:byte);
begin
   setlength(fnotes,count);
end;

function tNoteCollection.getlen:integer;
begin
   result:=length(fnotes);
end;

procedure tNoteCollection.Setnotes(Index: Integer; const anotes: TnoteStructure);
begin
  fnotes:=anotes;
end;

Implementation part:

function Tcis2opus.GetNotes(ClientInformationStructure:TClientInformationStructure)
              : tNoteCollection;
begin
  try
    result:=tNoteCollection.Create;
    result.setlen(1);
    result.notes[0]:=tnote.create;
    result.notes[0].NotetId:=inttostr(random(100));
    result.notes[0].AuthorUserName:='!1!'+ClientInformationStructure.ClientApplicationName;
    result.notes[0].SequenceNumber:=999;
  except
    on e:exception do addtolog(e.Message)
  end;
end;

Client

procedure TForm1.BitBtn1Click(Sender: TObject);
var c:tclientinformationstructure;
    r:tNoteCollection;
begin
  c:=tclientinformationstructure.Create;
  try
    c.ClientApplicationName:=labelededit1.Text;
    c.ClientApplicationPassword:=labelededit2.Text;
    c.RequestIdentifier:=labelededit3.Text;
    c.StartSequenceNumber:=strtointdef(labelededit4.Text,0);
    c.NumberOfNotes:=strtointdef(labelededit5.Text,0);
    r:=nil;
    r:=(HTTPRIO1 as iCIS2Opus).GetNotes(c);
      if r.getlen>0 then
        if assigned(r.notes[0]) then showmessage(r.notes[0].AuthorUserName);
  finally
    if assigned(c) then freeandnil(c);
    if assigned(r) then freeandnil(r);
  end;
end;
Taras
  • 391
  • 4
  • 10
  • 4
    Don't make us play spot-the-differences. Explain which parts you had to change to make your program work. – Rob Kennedy Sep 07 '12 at 17:29
  • This note shows exactly the change. See how the array was declared in the interface part and implemented the method to add items – Taras Sep 07 '12 at 21:09
  • `result:=tNoteCollection.Create` but who deallocates this? Is it destroyed after the call because it is a tremotable ? – nurettin May 17 '17 at 08:27
  • Minor point: the assigned check is not required for freeandnil, as free explicitly checks to see if the object is nil before calling destroy. – Robbie Matthews Jul 03 '17 at 01:57
2

Use SoapUI to send a request and see if anything is in the response. Working with objects is convenient, but sometimes you have to get down into the lower level and see the XML that's actually being sent. SoapUI is your friend here.

Chris Thornton
  • 15,620
  • 5
  • 37
  • 62
  • Chris, thanks for the answer. I installed SOAPUI. I got response But I don't know what does it means. Looks like response has only pointer to an object... – Taras Aug 11 '11 at 13:48