Sam M
Sam M

Reputation: 4166

What causes this error when consuming a web service?

I have a Delphi XE app that consumes a web service written in Cold Fusion (I have no control over the service's output format). I used the WSDL Importer in Delphi to create my unit for the calls to the web service. I am running into situations where I get an exception in Delphi that says "Element "data" does not contain a single text node".

The relevant portion of the XML coming back from the web service when I get the exception is this:

<data soapenc:arrayType="xsd:anyType[][1]" xsi:type="soapenc:Array">
  <data soapenc:arrayType="xsd:anyType[2]" xsi:type="soapenc:Array">
    <data xsi:type="soapenc:string">6490</data>
    <data xsi:type="soapenc:string">Other Expense</data>
  </data>
</data>

If the XML from the web service contains more than one <data> child, no exception occurs.

<data soapenc:arrayType="xsd:anyType[][3]" xsi:type="soapenc:Array">
  <data soapenc:arrayType="xsd:anyType[2]" xsi:type="soapenc:Array">
    <data xsi:type="soapenc:string">2600</data>
    <data xsi:type="soapenc:string">Deferred Revenue</data>
  </data>
  <data soapenc:arrayType="xsd:anyType[2]" xsi:type="soapenc:Array">
    <data xsi:type="soapenc:string">4120</data>
    <data xsi:type="soapenc:string">Non-Credit Income</data>
  </data>
  <data soapenc:arrayType="xsd:anyType[2]" xsi:type="soapenc:Array">
    <data xsi:type="soapenc:string">6490</data>
    <data xsi:type="soapenc:string">Other Expense</data>
  </data>
</data>

What causes this exception and is there a way around it without being able to change the web service itself?

Upvotes: 5

Views: 2605

Answers (3)

whosrdaddy
whosrdaddy

Reputation: 11860

Just for reference, I encountered the same problem today and after a few hours of searching I found the problem. The fact is that the WSDL importer maps certain types wrongly to string which results in the fact that TXMLDocument is instructed to read a textnode while there is none! So any type defined as string (or array of string) could be wrong...

To the OP: check the definition for the soapenc:Array type in your imported unit.

Upvotes: 2

Sam
Sam

Reputation: 2683

There must be a bug in your Delphi xml reading code. The fact it works sometimes is co-incidental. Navigating through XML is different depending on the component you're using.

I believe these will help you

Libraries and tutorials for XML in Delphi

Where is a tutorial for using XML with Delphi?

If you post the Delphi XML handling code, we could delve further.

Upvotes: 1

Chris Thornton
Chris Thornton

Reputation: 15817

I don't know what is causing the error, but yes, there is a way around it. You can use the RIO_AfterExecute() handler to modify the SOAPResponse, to alter the XML to "make it fit". It's an ugly, "bigger hammer" approach, but it ultimately lets you fiddle with the data to get around all sorts of problems.
Looking at your two examples, I'd try using stringreplace to replace 'xsd:anyType[][1]' with 'xsd:anyType[][3]'. If that doesn't work, try injecting another set of data with empty values, to make it seem like there's not just one.

You'll need a RIO object, and then you hook it up to a handler like this:

MyRIO.OnAfterExecute := self.RIO_AfterExecute;

In my case, "self" refers to a class that I've written around my SOAP stuff.

Be sure to set your position back to 0 when you're done fiddling with the request.

Here is some untested code:

procedure MyWrapper.RIO_AfterExecute(const MethodName: string; SOAPResponse: TStream);
var 
  SL : TStringList;   
begin
  // do stuff with the SOAPResponse here. 
  // It's a stream, so I like to load it into a stringlist
  // ex: 
    SL := TStringList.Create;
    try
      SOAPResponse.Position := 0;
      SL.LoadFromSTream(SOAPREsponse);
      // fiddle with stringreplace here, to doctor up the SL.text.
      SOAPResponse.Position := 0;
      SOAPResponse.size := length(SL.Text);
      SL.SaveToStream(SOAPResponse);
    finally
      SL.free;
    end;
end;

Upvotes: 4

Related Questions