I'm using CodeGear Rad Studio 2007 (Delphi Win32 2007) as a web services
consumer and Visual Studio 2003 (C#), .Net 1.1 as the web services server.
I can use the web service fine using Delphi, however I'd like to be able
to serialize/de-serialize some of the TRemotable based classes. The
current method that I'm using (basically the SoapToObject and
ObjectToSoap methods) doesn't seem to de-serialize properly. I've
serialized and then had a look at the xml and this seems ok, however I'm
having troubles loading this back into an object. I've described the
problem below with some code to explain.
If anyone can think of a better way for me to serialize/de-serialize
TRemotable classes, can you please share? Thanks
The problem code is the last procedure in this email (TForm3.Button2Click)
********* This code is the one imported from WSDL importer *************
BaseType = class(TRemotable)
private
published
end;
MonetaryItemType = class(BaseType)
private
FValue: TXSDecimal;
public
destructor Destroy; override;
published
property Value: TXSDecimal read FValue write FValue;
end;
TMyBankingType = class(TRemotable)
private
FGrossInterestAmt: MonetaryItemType;
FGrossInterestAmt_Specified: boolean;
FApportionedInterestAmt: MonetaryItemType;
FApportionedInterestAmt_Specified: boolean;
procedure SetGrossInterestAmt(Index: Integer; const
AMonetaryItemType: MonetaryItemType);
function GrossInterestAmt_Specified(Index: Integer): boolean;
procedure SetApportionedInterestAmt(Index: Integer; const
AMonetaryItemType: MonetaryItemType);
function ApportionedInterestAmt_Specified(Index: Integer): boolean;
public
destructor Destroy; override;
published
property GrossInterestAmt: MonetaryItemType Index (IS_OPTN)
read FGrossInterestAmt write SetGrossInterestAmt stored
GrossInterestAmt_Specified;
property ApportionedInterestAmt: MonetaryItemType Index (IS_OPTN)
read FApportionedInterestAmt write SetApportionedInterestAmt stored
ApportionedInterestAmt_Specified;
end;
************************************************************************
********* This is my code serializing and deserializing ****************
//xml document is a TXMLDocument put on the form, uses default //properties
//OpToSoapDomConvert1 is a TOpToSoapDomConvert on the form, uses default
//properties
//this procedure works fine, it gets the data that i want
//from the webservice and serializes the objects in an xml node
procedure TForm3.Button1Click(Sender: TObject);
var
soapService: MySoapService;
req: SoapRequest;
resp: SoapResponse;
errorMessage: WideString;
ARootNode, NewNode: IXMLNode;
RefId: WideString;
bankingData: TMyBankingType;
begin
XMLDocument1.Active := True;
soapService := GetMySoapService();
FillRequest(req);
try
try
resp := soapService.GetData(req, errorMessage);
ARootNode := XMLDocument1.CreateNode('MyRemotable');
bankingData := resp.BankingData;
newNode := bankingData.ObjectToSOAP(ARootNode,
ARootNode, OPToSoapDomConvert1, 'Test', 'Test',[], RefId);
XMLDocument1.DocumentElement := ARootNode;
Memo1.Lines.Clear;
Memo1.Lines.Add(XMLDocument1.XML.Text);
except
on E: Exception do
ShowMessage(E.Message);
end;
finally
FreeAndNil(req);
FreeAndNil(resp);
end;
end;
//this procedure is the problem, it seems to serialize the object but
//always returns a nil value for the value property of TMyBankingType
procedure TForm3.Button2Click(Sender: TObject);
var
data: TMyBankingType;
begin
XMLDocument1.XML.Text := Memo1.Text;
XMLDocument1.Active := True;
data := TMyBankingType.Create();
try
data.SOAPToObject(XMLDocument1.DocumentElement,
XMLDocument1.DocumentElement.ChildNodes[0], OPToSoapDomConvert1);
//access violation because Value is always nil!
ShowMessage(data.Value.DecimalString);
finally
FreeAndNil(data);
end;
end;
************************************************************************
Here's a sample code that illustrates how you could serialize an object to
and from XML:
const
sROOT_NODE = 'Root';
sROOT_NS = 'urn:TestNamespace';
function ObjToXML(const AObject: TRemotable; const ObjConverter:
IObjConverter): WideString;
var
Xdoc: IXMLDocument;
Root: IXmlNode;
RefId: WideString;
Namespace, TypeName: WideString;
IsScalar: Boolean;
begin
Xdoc := NewXMLDocument;
Root := Xdoc.AddChild(sROOT_NODE, sROOT_NS);
Root.DeclareNamespace(SXMLSchemaNameSpacePre, XMLSchemaNameSpace);
Root.DeclareNamespace(SXMLSchemaInstNameSpace99Pre,
XMLSchemaInstNameSpace);
RemClassRegistry.ClassToURI(AObject.ClassType, Namespace, TypeName,
IsScalar);
AObject.ObjectToSOAP(Root, Root, ObjConverter,
TypeName, Namespace, [], RefId);
Result := FormatXMLData(Xdoc.Node.XML);
end;
function XMLToObj(const XML: WideString; const ObjConverter: IObjConverter;
var AObject: TRemotable): Boolean;
var
Xdoc: IXMLDocument;
Root, Node: IXmlNode;
begin
Xdoc := LoadXMLdata(XML);
Root := Xdoc.ChildNodes.FindNode(sROOT_NODE, sROOT_NS);
Node := Root.ChildNodes[0];
AObject.SOAPToObject(Root, Node, ObjConverter);
Result := True;
end;
I'm copying the above from some testing code that we have. It may not be
exactly that you're after but should give you an idea. There's some
unnecessary code above (like formatting) that you'll probably want to
remove. Also, we create a fake root node. Typically that's some element
within the body (or header) of a SOAP envelope.
The one issue that I'm aware of is that you often have to clone a converter
because the API does not allow you (public) access to the method to reset
the converter. Really just the Convert Options are relevant in that case. So
we have some code to handle that too:
function CloneConverter(const Convert: TOPToSoapDomConvert): IObjConverter;
var
NewConv: TOPToSoapDomConvert;
begin
NewConv := TOPToSoapDomConvert.Create(nil);
Result := NewConv;
NewConv.Options := Convert.Options;
end;
function CloneConverter(const RIO: THTTPRIO): IObjConverter;
begin
Result := CloneConverter(RIO.Converter);
end;
Please let me know if the above does not answer your question and I'll
investigate some.
Cheers,
Bruneau.
I tried your code but it still doesn't seem to instantiate the "value"
property of the MonetaryItemType class (from my original example code).
I've also noticed that this is only if it happens to be of TXSDateTime
or TXSDecimal etc (basically any type that has TXS in front of it). If I
have string or int types of properties then it works fine!
Any word on why this is happening? Anybody have any alternatives to
serializing/deserializing classes to xml?
My apologies for the delay. I've been away from the newsgroup (dealing with
a cold) for several days. I'll try to post something later today.
Cheers,
Bruneau.
Hi,
I may be late, but I want to mention, thta in Delphi 2007 there is a
Property Converter for THTTPRIO. So we don´t need both CloneConverter
routines any more.
Thank you for posting the nice example. Much appreciated