Web Services Quiz: Issue 2 – the Answer Part 2


In addition to the architectural issue, let’s talk about two implementation problems:


 


First, the serializing problem


You always have to be aware of the fact that the Web Service’s ultimate type system is the XML type system. Given our example, the request schema type looks the following:


 


<s:schema elementFormDefault="qualified" targetNamespace="uri.beatsch.Issue2">


   <s:element name="dpr" type="s0:DeletePersonRequest" />


   <s:complexType name="DeletePersonRequest">


      <s:sequence>


         <s:element minOccurs="0" maxOccurs="1" name="name" type="s:string" />


         <s:element minOccurs="0" maxOccurs="1" name="firstname" type="s:string" />


         <s:any minOccurs="0" maxOccurs="unbounded" />


      </s:sequence>


   </s:complexType>


</s:schema>


 


Regrettably, the generated type is ambiguous: Both, name and firstname have their minOccurs set to “0”. This by itself is no problem, but as soon as they are followed by an ANY element the deserializer is in trouble: There is now way for this guy to detect whether name and firstname contain to the immediately following ANY element or not. You can solve this problem by changing .NET’s default behavior and set the IsNullable property of the XmlElement attribute to true:


 


public class DeletePersonRequest


{


   [XmlElement(IsNullable = true)]


   public string name;


   [XmlElement(IsNullable = true)]


   public string firstname;


   [XmlAnyElement]


   public XmlElement[] AnyElem;


}


 


By doing so, the generated schema looks the following:


 


<s:schema elementFormDefault="qualified" targetNamespace="uri.beatsch.Issue2">


   <s:element name="dpr" type="s0:DeletePersonRequest" />


   <s:complexType name="DeletePersonRequest">


      <s:sequence>


         <s:element minOccurs="1" maxOccurs="1" name="name" nillable="true" type="s:string" />


         <s:element minOccurs="1" maxOccurs="1" name="firstname" nillable="true" type="s:string" />


         <s:any minOccurs="0" maxOccurs="unbounded" />


      </s:sequence>


   </s:complexType>


</s:schema>


 


Now, the ambiguity has been resolved. But why do we have to do that? The answer is, because .NET’s default behavior for reference types doesn’t serialize null references. If a field is null, the corresponding XML element won’t be serialized. Note, that this works different for value types: Since a value type never can be null, they will be always serialized and therefore their minOccurs is always set to 1.


 


Second, the versioning or decoupling problem:


The message contains an ANY element to carry additional unknown elements. That’s great, since this message can be used within processes where different endpoints accept different versions of the very same message. But how do we carry additional, unknown attributes? For doing so we have to add a field of type XmlAnyAttribute[] to the message:


 


public class DeletePersonRequest


{


   [XmlElement(IsNullable = true)]


   public string name;


   [XmlElement(IsNullable = true)]


   public string firstname;


   [XmlAnyElement]


   public XmlElement[] AnyElem;


   [XmlAnyAttribute]


   public XmlAttribute[] AnyAttrib;


}


 


Now, even unknown attributes can be carried around!


 


That’s it so far from my side. Any other recognized problems?


 

Comments (3)

  1. Anonymous says:

    What does the client code look like?

    Does the client have to pass an XmlElement[] for AnyElem, or can he pass an Object[]?

  2. Anonymous says:

    The consumer can pass what ever he wants, because we’re not going to process these elements. But if we would delegate the message to another service the additional elements and attributes may make sense to them.

Skip to main content