Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 586
Sam M Avatar asked Aug 24 '11 21:08

Sam M


2 Answers

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;
like image 69
Chris Thornton Avatar answered Sep 17 '22 00:09

Chris Thornton


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.

like image 39
whosrdaddy Avatar answered Sep 21 '22 00:09

whosrdaddy