0

This is a long one - apologies....

Is there a reliable way of returning lists from C# to VBA via a web service?

I've got a web service that returns a 'result', which consists of some doubles, strings and so on (it's an engineering calculation). I also return a list of 'Errors' and 'Messages' which contain information on the calc and give you some idea of what, if anything, went wrong. So these lists are potentially empty.

Here's my C# 'NoteList' class, and both the 'Errors' and 'Messages' are of this type.

public class Note
{
    public int ID;
    public string Message;

    public Note()
    {
        ID = 0;
        Message = "";
    }

    public Note(int aID, string aMessage)
    {
        ID = aID;
        Message = aMessage;
    }
}

public class NoteList : List<Note>
{
}

All pretty straightforward.

My 'Result' class contains two lists, thus:

public class Result
{
    // Lists
    public NoteList ErrorList = new NoteList();
    public NoteList MessageList = new NoteList();

    // Lots of other stuff...
}

So far, so good...

I've used the VBA Web References Toolkit to construct some VBA proxy 'struct' to hold a result. The generated code looks like this...

A 'struct_note':

Public ID As Long
Public Message As String

A 'struct_Result':

'"ErrorList" is an array with elements defined as struct_Note
'See Complex Types: Arrays in Microsoft Office 2003 Web Services Toolkit Help
'for details on implementing arrays.
Public ErrorList As Variant
'"MessageList" is an array with elements defined as struct_Note
'See Complex Types: Arrays in Microsoft Office 2003 Web Services Toolkit Help
'for details on implementing arrays.
Public MessageList As Variant

' plus the other stuff....

I've included the comments in the above listing as generated by the Toolkit.

In my VBA I create my result and use it as follows:

Dim r as struct_Result
Dim ws As clsws_MyWebService ' This is another generated proxy class

and I populate it thus:

Set r = ws.wsm_SomeMethod()

do stuff and then 'nothing' it when I'm done

Set r = nothing

Right - to my problem...

I want to run this web method a gazillion times on an Excel sheet and note the errors and messages that come back. So I put the above in a loop

for i = 1 to 10000
   Set r = ws.wsm_SomeMethod()
   ' do some analysis and write back to the spreadsheet
   Set r = nothing
next i

This works fine UNTIL I get back a result which has no errors in it, ie the ErrorList is empty. I then get a VB error saying

Screen shot of VB error
(source: bigsmoke.com)

I suspect that VB is constructing something that requires an ErrorList with something in it and complains that the array is of zero size.

Anyone out there in stackoverflow land got any ideas as to how to solve this?

Thanks

sal

-------- [EDIT] BELOW ADDED FOLLOWING RESPONSE FROM nitzmahone -------------------

I've modified my code to permit a null list as follows

public class Result
{
    // Lists
    [XmlElement(IsNullable = true)]
    public NoteList ErrorList = new NoteList();

    [XmlElement(IsNullable = true)]
    public NoteList MessageList = new NoteList();

    // Lots of other stuff...
}

This modifies the WSDL in a way I wasn't expecting. Prior to adding the IsNullable attributes the WSDL generated for a 'Result' looked like:

  <s:complexType name="Result">
    <s:sequence>
      <s:element minOccurs="0" maxOccurs="1" name="ErrorList" type="tns:ArrayOfNote" />
      <s:element minOccurs="0" maxOccurs="1" name="MessageList" type="tns:ArrayOfNote"/>
      // Stuff deleted for clarity...
    </s:sequence>
  </s:complexType>
  <s:complexType name="ArrayOfNote">
    <s:sequence>
      <s:element minOccurs="0" maxOccurs="unbounded" name="Note" nillable="true" type="tns:Note" />
    </s:sequence>

We've got a 'Result' containing an 'ArrayOfNote' containing nullable 'Notes'.

After adding the [XmlElement(IsNullable = true)] to the Lists I get this WSDL:

  <s:complexType name="Result">
    <s:sequence>
      <s:element minOccurs="0" maxOccurs="unbounded" name="ErrorList" nillable="true" type="tns:Note" />
      <s:element minOccurs="0" maxOccurs="unbounded" name="MessageList" nillable="true" type="tns:Note" />
      // Stuff deleted for clarity...
    </s:sequence>
  </s:complexType>

I've lost the "ArrayOfNote" and I'm left with a single (ie non array) 'ErrorList' which is a 'Note'. I've run some sample cases and indeed with the addition of the [XmlElement(IsNullable = true)] to the Lists I get back (in the VB) a single value which is always the last element of the server side C# lists.

Should I be putting the [XmlElement(IsNullable = true)] somewhere else? Where??

Thanks!

Steve

2
  • Sorry, I suck- since the thing was a list, you should use [XmlArray(IsNullable=true)], not XmlElement. Commented Sep 29, 2009 at 21:39
  • Thanks for that - I tried it out and did indeed get what I think is the correct WSDL. But the VBA proxy classes still require an entry in the lists otherwise the same error occurs. I reckon it's a fault in the VBA proxy class generator? Commented Oct 1, 2009 at 11:02

2 Answers 2

1

The XML deserializer code generated by the VBA Web Reference tool is very literal about the order and format of the XML it consumes (based on the WSDL it was generated from). When one of your lists in the result object is empty, the server isn't generating a container element for it, and the VBA deserializer isn't prepared to handle the missing element. Try marking both lists in the Result class (on the service side) with [XmlArray(IsNullable=true)] - this will cause an empty container element to be generated with an xsi:nil attribute when one (or both) is empty, keeping the VBA deserializer happy.

Sign up to request clarification or add additional context in comments.

2 Comments

Forgive the formatting if this looks odd... Can you give me an example of where to add this attribute? I tried public class Result { // Lists [XmlElement(IsNullable = true)] public NoteList ErrorList = new NoteList(); [XmlElement(IsNullable = true)] public NoteList MessageList = new NoteList(); // Lots of other stuff... } but I get a Error "'System.Xml.XmlElement' is not an attribute class"???
<SLAP ON HEAD>Forgot to add "using System.Xml.Serialization"...</SLAP ON HEAD>
0

Ypu can not return a generic list. this is .net specific. I have developed alot of Web services used by thrid parties and I always had to change a list to an array. Which is easy to do to be used by another language system.

myList.ToArray().

1 Comment

Thanks Jonathan, I'll have a look at using Arrays.... However in my specific problem the VBA DOES correctly map the C# list to a VB array of objects EXCEPT when the list is empty. The most obvious Horrible Hack would be to force my lists to have a meaningless zeroth element. If I do that (and I've checked that this is true) then VBA has no problem accessing the lists. But that's horrible! So - assuming I can keep my Lists, anyone got any ideas how to tell VBA to cope with an empty list?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.