Link to home
Start Free TrialLog in
Avatar of AlJeek
AlJeek

asked on

Please Help!: How do I get an attribute value from a soap message in vb .net??

Hi all,

A soap message returned by an axis (Java/Apache) web service contains elements with attributes.

e.g.

<Values Count="3">
<Value>Hello</Value>
<Value>World</Value>
<Value>!!!</Value>
</Values>

could be part of the message.   How do I get the attribute value in vb .net?  The web service is defined by a wsdl which I need to update to show that the attribute is there, but i'm not sure how.

Part of the wsdl defining the above would be:
<xs:complexType name="Values">
  <xs:complexContent>
    <xs:restriction base="soapenc:Array">
      <attribute ref="soapenc:arrayType" wsdl:arrayType="typens:Vallue>
    </xs:restriction>
  </xs:complexContent>
</xs:complexType>

<xs:complexType name="Value">
  <xs:element name="Value" type="xs:string"/>
</xs:complexType>

How can I add the attribute to it.  VB net currently reads in Values as an array.

Thanks!
Al.
Avatar of rdcpro
rdcpro
Flag of United States of America image

The attribute goes outside of the content model for your value element:

<xs:complexType name="Value">
      <xs:sequence>
            <xs:element name="Value" type="xs:string" minOccurs="1" maxOccurs="unbounded"/>
      </xs:sequence>
      <xs:attribute name="Count" type="xs:int" use="required"/>
</xs:complexType>

Regards,
Mike Sharp
Avatar of AlJeek
AlJeek

ASKER

When I try that, VB .NET says:
"Use elements (not attributes) for fields/paramaters"
Avatar of AlJeek

ASKER

Incase it's relevant, our model is more like:

<xs:complexType name="Values">
  <xs:complexContent>
    <xs:restriction base="soapenc:Array">
      <attribute ref="soapenc:arrayType" wsdl:arrayType="typens:Vallue>
    </xs:restriction>
  </xs:complexContent>
</xs:complexType>

<xs:complexType name="Value">
  <xs:element name="AValue1" type="typens:AValue1"/>
  <xs:element name="AValue2" type="typens:AValue2"/>
</xs:complexType>

etc.
Hmmm...I was afraid you'd run into that issue.  It seems to me that if you want to specify the size of the array, you'd do it like:

<Values xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="xsd:string[5]">
  <Value>Mike</Value>
  <Value>Sharp</Value>
  <Value>string</Value>
  <Value>cheese</Value>
  <Value>food</Value>
</numbers>

But SOAP 1.2 has a few more attributes for describing arrays:

  <!-- Array attributes. Needed to give the type and dimensions of an array"s contents, and the offset for partially-transmitted arrays. -->

  <xs:simpleType name="arraySizeBase" >
    <xs:annotation>
        <xs:documentation>
        A list type that allows * and non negative integers. Used as the
    base type for arraySize below.
        </xs:documentation>
      </xs:annotation>
    <xs:list>
        <xs:simpleType>
          <xs:union memberTypes="xs:nonNegativeInteger" >
              <xs:simpleType>
                <xs:restriction base="xs:token" >
                    <xs:enumeration value="*" />
                  </xs:restriction>
              </xs:simpleType>
            </xs:union>
        </xs:simpleType>
      </xs:list>
  </xs:simpleType>


  <xs:simpleType name="arraySize" >
    <xs:annotation>
        <xs:documentation>
        Pattern based restriction of the arraySizeBase list type. Used
    as the type of the arraySize attribute. Restricts asterisk ( * )
    to first list item only. Instances must contain at least an
    asterisk ( * ) or a nonNegativeInteger. May contain other
    nonNegativeIntegers as subsequent list items.
      Valid instances include;
      
        *
        1
        * 2
        2 2
        * 2 0

        </xs:documentation>
      </xs:annotation>
    <xs:restriction base="tns:arraySizeBase" >
        <xs:pattern value="(\*|(\d+))(\s+\d+)*" />
      </xs:restriction>
  </xs:simpleType>
   
  <xs:attribute name="arraySize" type="tns:arraySize" />
  <xs:attribute name="itemType" type="xs:QName" />

  <xs:attributeGroup name="arrayAttributes" >
    <xs:attribute ref="tns:arraySize" />
      <xs:attribute ref="tns:itemType" />
  </xs:attributeGroup>    
 
Maybe these will help?

Mike Sharp
Avatar of AlJeek

ASKER

Thanks Mike, that's very useful.

Count is an example attribute we have, but not a vital one, since I expect that clients that connect to our webservice will be able to count the array size themselves.  From what I've seen too, vb .net seems to not care if you send the size as you showed and just counts the children (we sent 0 everytime and it still counted it right).

What we do need really need however is to know how to specify attributes that have nothing to do with arrays themselves, but describe some useful information about the array or its elements.

For example, say we needed a client to specify an option.  We could send them a soap message which could include:

<Values>
  <Option index="1" optionName="A">
    <Value>Hello</Value>
    <Value>World!</Value>
  </Option>
  <Option index = "2" optionName="B">
    <Value>Whatever</Value>
    <Value>Really</Value>
  </Option>
....etc
</Values>

Again you could argue that they don't need the index if we send the options in order (and they take for granted that the first option is the first in the array).  But how would we get the optionName attribute?

We want to change the structure of the soap message given by the webservice as little as possible at the moment, so no extra parent/child elements please (although this would make more sense).

Thanks again!

Aled.
ASKER CERTIFIED SOLUTION
Avatar of rdcpro
rdcpro
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of AlJeek

ASKER

Thanks for the info Mike.

I was afraid of that, I guess we're going to have to change the structure of the message so that an array has a parent element.  This way, we don't need attributes.

I don't want to get into direct xml access, as our customers will be writing their own clients to connect to our webservice, and we want it to be as easy as possible.

Not sure what you mean by using a structure though, how would I incorporate one into a wsdl?

Thanks,
Aled.
A struct is a complex type, like an array.  It can have repeating members, as well.  While an array has a position accessor, you access structs by element name and/or position.  In .NET, you can deserialize directly to a struct, so the implementation details of connecting your web service to the messaging are totally hidden.  You simply create a web method that takes the struct object as it's parameter.  If you have a schema, wsdl.exe will generate stub classes that essentially do the same thing, but they're classes rather than structs.  They can be tweaked to fit, though.  Once you've got all this written, ASP.NET will autogenerate the WSDL for you.  The Microsoft Office InfoPath 2003 SDK has an excellent example of using structs for this.  It's a purchase order web service, and here's an abbreviated example (in C#, however):

The basic part of the message is a rootPO struct, defined here:

        [System.Xml.Serialization.XmlRootAttribute(ElementName="root", IsNullable=false)]
        public struct rootPO
        {
            public routingInfo routingInfo;
            public purchaseRequestPO purchaseRequest;

            [System.Xml.Serialization.XmlAnyAttributeAttribute()]
            public System.Xml.XmlAttribute[] AnyAttr;
        }
 
Skipping the routingInfo stuff, the purchaseRequestPO is:

        [System.Xml.Serialization.XmlRootAttribute(ElementName="purchaseRequest", IsNullable=false)]
        public struct purchaseRequestPO
        {
            public int number;

            [System.Xml.Serialization.XmlElementAttribute(DataType="date")]
            public System.DateTime dateOpened;

            public string chargeTo;

            public string group;

            public string name;

            public string email;

            [System.Xml.Serialization.XmlArrayItemAttribute(ElementName="item", IsNullable=false)]
            public itemPO[] list;

            public System.Single total;
        }

As you can see, it has a few public string members (exposed as properties of the purchaseRequest instance).  It also has an array, itemPO[].

It goes on from there, but the serialization attributes tell ASP.NET how to deserialize the incoming message to the rootPO object.  The web method to process a PO starts out like:

        [WebMethod]
        public bool ProcessPO(rootPO PO)
        {
            bool fValidPO = true;
            SortedList aiPartNumbers;
            string sPartNumbers;
           
            int i = 0;

            int iPartNumber;
            int iQuantityRequested;
            int iQuantityAvailable;

            string sConnect;
            string sSqlQuery;
            OleDbConnection oOleDbConn;
            OleDbCommand oOleDbCommand;
            OleDbDataReader oOleDbReader;

            try
            {
                PO.purchaseRequest.number = GetPurchaseOrderNumber(PO);
               
                // Build a hash table of requested part numbers and their associated index
                // in the product list in the PO request.
                aiPartNumbers = new SortedList();
                sPartNumbers = "";
                for (i=0; i<PO.purchaseRequest.list.Length; i++)


and goes on from here.  This might be more what you're looking for, I think.  Download the SDK, and you can run the solution (you may have to edit the sln file to point to the right server--some of the SDK samples didn't run directly from the solution without some editing--at least for me.

Regards,
Mike Sharp
Avatar of AlJeek

ASKER

Thanks a lot Mike, will give it a go!

Cheers,
Aled.