Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

help calling C# web service

175 views
Skip to first unread message

Charles Vinal

unread,
Mar 26, 2007, 5:54:25 PM3/26/07
to
I've spent three days trying to figure out how to call a C# web service from
delphi. The web service expects that a system.xml.xmlnode type will be
passed to it (that is how it is called from C# client). When I import the
service, the parameter is created as a Tremotable type with a widestring
property. When I compare the expected soap envelope to what the Delphi
service is creating, it is identitical (I am assigning the appropriate xml
text to the widestring property). No matter how different ways I try, I
always get an error stating that an instance of the object does not exist.
Help would be very, very, appreciated!

Charlie


Charles Vinal

unread,
Mar 26, 2007, 6:19:28 PM3/26/07
to
Forgot to add: here is the code that works in c#

private void button1_Click(object sender, System.EventArgs e)
{
a2zService.a2zAuthenticationHeader a2zHeader = new
a2zService.a2zAuthenticationHeader();
a2zService.a2zExhibitorList a2zServ = new a2zService.a2zExhibitorList();

a2zHeader.Key = "[omitted]";
a2zServ.a2zAuthenticationHeaderValue = a2zHeader;
XmlDocument xmlRequest = new XmlDocument();
xmlRequest.LoadXml("<as></as>");
XmlNode root = xmlRequest.FirstChild;
XmlNodeReader response = new
XmlNodeReader(a2zServ.getExhibitorList(root));
}

"Charles Vinal" <cvi...@euclidtechnology.com> wrote in message
news:46084161$1...@newsgroups.borland.com...

Jean-Marie Babet

unread,
Mar 26, 2007, 6:35:40 PM3/26/07
to
Hello Charles,

I'm not sure whether that's the case, but if in .NET (C# or other) you
create a webservice that exposes a System.Xml.XmlNode (or
System.Xml.XmlDocument), the service will typically expose the complex type
as "mixed=true" which Delphi's importer maps to 'WideString'. Unfortunately,
this will cause the data to be encoded in the XML packet. The service at the
other end will probably not handle this.

For example, if you send the following:

<client xmlns="urn:cust">
<nom>Racine</nom>
<titre genre="tragedie">Andromaque</titre>
</client>

Instead of the above, the XML packet will really contain:

&lt;client xmlns="urn:cust"&gt;&lt;nom&gt;Racine&lt;/nom&gt;&lt;titre
genre="tragedie"&gt;Andromaque&lt;/titre&gt;&lt;/client&gt;


It's something that I've been working on remedying recently (it just fell of
the list for D2007). The new importer maps mixed complex types to a new
type, TXMLData:


{ For cases where one needs access to the raw XMLNode data in a SOAP
envelope }
TXMLData = class(TRemotable)
private
FXMLDocument: IXMLDocument;
FXMLNode: IXMLNode;
public
constructor Create; override;
destructor Destroy; override;
function ObjectToSOAP(RootNode, ParentNode: IXMLNode;
const ObjConverter: IObjConverter;
const Name, URI: InvString; ObjConvOpts:
TObjectConvertOptions;
out RefID: InvString): IXMLNode; override;
procedure SOAPToObject(const RootNode, Node: IXMLNode; const
ObjConverter: IObjConverter); override;
procedure LoadFomXML(const XML: string); overload;
procedure LoadFromXML(const XML: WideString); overload;

property XMLNode: IXMLNode read FXMLNode;
end;


We have not fully tested the above but it's checked in and I have had
unit-tests that illustrate the usage running for a few days. If you're
interested, I'd be happy to make TXMLData and the new importer available.

There are two other solutions to this:

#1. Keep the WideString and decode the data in the AfterExecute event.

#2 Have a TRemotable-derived class (like TXMLData) that you can created with
the string to send and that sends that string in ObjectToSOAP. TXMLData is
bulky because it has to clone any data into its own XML document. That's
because its lifetime can't be tied to that of a request/response. However,
since you're just sending data, you need a class with just a string member
that copies the string to the node.

Let me know if the above is accurate and if I can help with any approach
you'd like to try.

Cheers,

Bruneau.


Charles Vinal

unread,
Mar 27, 2007, 9:56:13 AM3/27/07
to
Hi Bruneau,

Thanks for your reply. I have tried to implement using a tRemotable with a
property of ixmlnode and also a tremotable with wide string property. When I
use remotable.widestring method and preview the soap packet, it looks
perfect (I replace the &gt; etc with the appropriate string before sending).
However, I still get an error from the server stating that there is no
object instance (hence why I think it has to be passed an actual instance of
a system.xml.xmlnode object). It seems that it is expecting to directly
write to a system.xml.xmlnode structure that is being passed (which I can't
seem to mimic). I would really appreciate any help you can provide. The wsdl
is located at:
http://dev3.a2zinc.net/ww5root/ezscte/dataservices/public/ExhibitorProvider.asmx?wsdl. I
can send you the code that I have as well.

I would also be interested in getting the code for the txmldata - it seems
almost all of the .net services we have to access us the xmldocument or
xmlnode (we don't have any control over the types - I would prefer a simple
widestring - but the vendor does it this way).

Thanks,

Charlie

"Jean-Marie Babet" <bba...@borland.com> wrote in message
news:46084abb$1...@newsgroups.borland.com...

Jean-Marie Babet

unread,
Mar 27, 2007, 1:46:46 PM3/27/07
to
Hello,

Below is a unit, XSXMLData.pas that contains TXMLData extracted. It's a
TRemotable-based type that holds on to an XMLDocument. It writes and reads
the content of the document to and from the SOAP Envelope. It's not an ideal
solution as it duplicates data and has to clone nodes into and out of the
document of the envelope. However, that was required for it to outlive the
document that contained the XML data sent.

To send XML data, you'll use something along the lines of:

DataIn := TXMLData.Create;
try
DataIn.LoadFromXML(XMLStr);
AWebService.methodThatTakesXML(DataIn);


When receiving data, you can access the XML data via the XMLNode property:

DataOut := AWebService.methodThatReturnsXML(....);
try
ShowMessage(DataIn.XMLNode.XML);


Replace the 'WideString' that represent XML data with TXMLData in the file
generated by the importer. For example:

Request = TXMLData;
Response = TXMLData;

Please let me know if you need more information or run into difficulties
with the changes.

Cheers,

Bruneau.

====== [XSXMLData.pas ] ======

unit XSXMLData;

interface

uses SysUtils, InvokeRegistry, Classes, XmlIntf;

type

{ For cases where one needs access to the raw
XMLNode data in a SOAP envelope }
TXMLData = class(TRemotable)
private
FXMLDocument: IXMLDocument;
FXMLNode: IXMLNode;
public
constructor Create; override;
destructor Destroy; override;
function ObjectToSOAP(RootNode, ParentNode: IXMLNode;
const ObjConverter: IObjConverter;
const Name, URI: InvString;
ObjConvOpts: TObjectConvertOptions;
out RefID: InvString): IXMLNode; override;
procedure SOAPToObject(const RootNode, Node: IXMLNode;
const ObjConverter: IObjConverter); override;
procedure LoadFomXML(const XML: string); overload;
procedure LoadFromXML(const XML: WideString); overload;

property XMLNode: IXMLNode read FXMLNode;
end;

implementation

uses SOAPConst,
{$IFDEF MSWINDOWS}Windows{$ENDIF}
{$IFDEF LINUX}Libc{$ENDIF},
DateUtils, XMLDoc;

{ TXMLData }

constructor TXMLData.Create;
begin
inherited;
FXMLDocument := NewXMLDocument;
end;

destructor TXMLData.Destroy;
begin
FXMLNode := nil;
FXMLDocument := nil;
inherited;
end;

procedure TXMLData.LoadFomXML(const XML: string);
begin
FXMLDocument.LoadFromXML(XML);
if FXMLDocument.Node.ChildNodes.Count > 0 then
FXMLNode := FXMLDocument.Node.ChildNodes[0];
end;

procedure TXMLData.LoadFromXML(const XML: WideString);
begin
FXMLDocument.LoadFromXML(XML);
if FXMLDocument.Node.ChildNodes.Count > 0 then
FXMLNode := FXMLDocument.Node.ChildNodes[0];
end;

{ Utility routine lifted from XMLDoc.pas }
function CloneNodeToDoc(const SourceNode: IXMLNode;
const TargetDoc: IXMLDocument;
Deep: Boolean = True): IXMLNode;
var
I: Integer;
begin
with SourceNode do
case nodeType of
ntElement:
begin
Result := TargetDoc.CreateElement(NodeName, NamespaceURI);
for I := 0 to AttributeNodes.Count - 1 do
Result.AttributeNodes.Add(CloneNodeToDoc(AttributeNodes[I],
TargetDoc, False));
if Deep then
for I := 0 to ChildNodes.Count - 1 do
Result.ChildNodes.Add(CloneNodeToDoc(ChildNodes[I],
TargetDoc, Deep));
end;
ntAttribute:
begin
Result := TargetDoc.CreateNode(NodeName,
ntAttribute,
NamespaceURI);
Result.NodeValue := NodeValue;
end;
ntText, ntCData, ntComment:
Result := TargetDoc.CreateNode(NodeValue, NodeType);
ntEntityRef:
Result := TargetDoc.createNode(nodeName, NodeType);
ntProcessingInstr:
Result := TargetDoc.CreateNode(NodeName,
ntProcessingInstr,
NodeValue);
ntDocFragment:
begin
Result := TargetDoc.CreateNode('', ntDocFragment);
if Deep then
for I := 0 to ChildNodes.Count - 1 do
Result.ChildNodes.Add(CloneNodeToDoc(ChildNodes[I],
TargetDoc,
Deep));
end;
else
{ntReserved, ntEntity, ntDocument, ntDocType:}
{ XMLDocError(SInvalidNodeType); }
end;
end;

function TXMLData.ObjectToSOAP(RootNode, ParentNode: IXMLNode;


const ObjConverter: IObjConverter; const Name, URI: InvString;
ObjConvOpts: TObjectConvertOptions; out RefID: InvString): IXMLNode;

var
RawNode: IXMLNode;
begin
Result := inherited ObjectToSOAP(RootNode,
ParentNode,
ObjConverter,
Name, URI,
ObjConvOpts+[ocoDontSerializeProps],
RefId);
if (Result <> nil) and (FXMLNode <> nil) then
begin
RawNode := CloneNodeToDoc(FXMLNode,
ParentNode.OwnerDocument,
True);
if RawNode <> nil then
Result.ChildNodes.Add(RawNode);
end;
end;

procedure TXMLData.SOAPToObject(const RootNode, Node: IXMLNode;
const ObjConverter: IObjConverter);
begin
if Node.ChildNodes.Count = 1 then
FXMLNode := CloneNodeToDoc(Node.ChildNodes[0],
FXMLDocument, True)
end;

initialization
RemClassRegistry.RegisterXSClass(TXMLData,
XMLSchemaNameSpace,
'schema', '',
False,
ocNoMultiRef);

end.


"Charles Vinal" <cvi...@euclidtechnology.com> wrote in message

news:460922ce$1...@newsgroups.borland.com...

Charles Vinal

unread,
Mar 27, 2007, 2:17:24 PM3/27/07
to
Not to overstate things, BUT YOU ARE THE GREATEST HUMAN BEING EVER!
(the xmldata.pas file worked).

Thank you 1 million times over.

Charlie

"Jean-Marie Babet" <bba...@borland.com> wrote in message

news:46095885$1...@newsgroups.borland.com...

Jean-Marie Babet

unread,
Mar 27, 2007, 3:49:14 PM3/27/07
to
Thanks for the feedback. QA has not had a chance to look over some of the
changes I've checked in since D2007. So it's good to hear that it works
somewhere else besides "on my machine".

Cheers,

Bruneau.


Charles Vinal

unread,
Mar 28, 2007, 10:18:53 AM3/28/07
to
Hi Bruneau,

I am surprised that this was not a top priority to get included into D2007 -
it seems a lot of the web services out there expect an actual XML document
as opposed to a widestring that contains XML data. This is probably due to
the ability to directly bind an xmldocument or xmlnode to .net and /or ajax
controls. If you have updates to the xmldata.pas file, please let me know!

Regards,

Charlie

"Jean-Marie Babet" <bba...@borland.com> wrote in message

news:46097539$1...@newsgroups.borland.com...

Jean-Marie Babet

unread,
Mar 28, 2007, 8:06:42 PM3/28/07
to
Hello,

I'm running into an increasing number of WebServices that do pass raw XML
data but for D2007 (well, back when we were actually aiming at Highlander)
we made a list of the most requested Services Delphi users reported
[MapPoint/Amazon/eBay/etc]... then I built some C#/Axis services that
focused/illustrated the key problems we had.

TXMLData was on the list for two things: (a) complex types with mixed="true"
[that the raw XML data case] and (b) xsd:any support. ebay does use xsd:any
but my limited tests never found a case where 'any' content was sent. So at
that point TXMLData fell off the list. I would probably say that full
support for xsi:nil="true" node is the one item that would have come first
if I had the time.

Fortunately, these things are being addressed [although I think I'm not
supposed to say that:( - so I'll stop before I get myself in trouble].

Cheers,

Bruneau.


"Charles Vinal" <cvi...@euclidtechnology.com> wrote in message

news:460a799f$1...@newsgroups.borland.com...

0 new messages