Inquisitive Chap thoughts recycling site

17Mar/112

A little bit of magic with minOccurs, XML Schema and C# deserialization

Usually when you have to deal with a XML Schema types are fairly simple.

<?xml version="1.0" encoding="utf-8"?><xsd:schema
   elementFormDefault="qualified" targetNamespace="http://inquisitivechap.com/xsd"
   xmlns:me="http://inquisitivechap.com/xsd" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <xsd:element name="root">
      <xsd:complexType>
         <xsd:sequence>
            <xsd:element name="optionalString" type="xsd:string" minOccurs="0" maxOccurs="1" />
         </xsd:sequence>
      </xsd:complexType>
   </xsd:element>
</xsd:schema>

With a little bit of help of a xsd.exe (Xml Schemas/Data Types support utility)

(xsd.exe test.xsd /c) 

or just by playing with code you will get a simple class

[Serializable]
[DebuggerStepThrough]
[DesignerCategory("code")]
[XmlType(AnonymousType=true, Namespace="http://inquisitivechap.com/xsd")]
[XmlRoot(Namespace="http://inquisitivechap.com/xsd", IsNullable=false)]
public partial class root
{
   public string optionalString { get; set; }
}

Such a class can be then easily used in standard deserialisation scenario:

using System;
using System.Xml;
using System.Xml.Serialization;
using System.IO;

namespace testXml
{
   class Program
   {
       private static Byte[] StringToUTF8ByteArray(String pXmlString)
       {
           var encoding = new UTF8Encoding();
           var byteArray = encoding.GetBytes(pXmlString);
           return byteArray;
       }
       public static T DeserializeObject<T>(String xml)
       {
           var xs = new XmlSerializer(typeof(T));
           var memoryStream = new MemoryStream(StringToUTF8ByteArray(xml));
           var xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
           return (T) xs.Deserialize(memoryStream) ;
       }
       static void Main(string[] args)
       {
           var rootElement = DeserializeObject<root>("<root xmlns=\"http://inquisitivechap.com/xsd\"></root>");
       }
   }
}

However what about a little bit more complex XML Schema in which we would like to utilise something so trivial as int or simpleType and restriction of an element with minOccurs="0"?
Let's extend our schema, by optional int and special type me:types in me namespace and the restrictions.

<?xml version="1.0" encoding="utf-8"?>
<xsd:schema
   elementFormDefault="qualified" targetNamespace="http://inquisitivechap.com/xsd"
   xmlns:me="http://inquisitivechap.com/xsd" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <xsd:element name="root">
      <xsd:complexType>
         <xsd:sequence>
            <xsd:element name="optionalString" type="xsd:string" minOccurs="0" maxOccurs="1" />
            <xsd:element name="optionalInt" type="xsd:int" minOccurs="0" maxOccurs="1" />
            <xsd:element name="optionalType" type="me:types" minOccurs="0" maxOccurs="1" />
         </xsd:sequence>
      </xsd:complexType>
   </xsd:element>

   <xsd:simpleType name="types">
      <xsd:restriction base="xsd:string">
         <xsd:enumeration value="TypeA" />
         <xsd:enumeration value="TypeB" />
         <xsd:enumeration value="TypeC" />
      </xsd:restriction>
   </xsd:simpleType>
</xsd:schema>

Trying to write a class that will handle that from hand is almost impossible. In case of XML Schema's primitive datatypes it is no-brainer.
All you need to do is try to deserialise and you will need to check if value of property representing optional element of deserialised XML is not null.

However, for these tricky minOccurs, be aware that there is a magic going on - a special property with a postfix Specified needed to be present.
So for optionalInt from our example we need optionalIntSpecified, respectively for the optionalType we would need optionalTypeSpecified.
In both cases the *Specified property need to be marked with XmlIgnore attribute (otherwise you might want to deserialise / serialise it as well).

It is also worth to note that you won't be able to create any element with Specified prefix as it has special meaning.
One of these thing that you never wanted to know about .net, deserialisation, passing data in XML format, etc.

For reference the code that will handle deserialisation of any XML without all elements that could be minOccurs="0":

[Serializable]
[DebuggerStepThrough]
[DesignerCategory("code")]
[XmlType(AnonymousType=true, Namespace="http://inquisitivechap.com/xsd")]
[XmlRoot(Namespace="http://inquisitivechap.com/xsd", IsNullable=false)]
public partial class root
{
   public string optionalString { get; set; }
   public int    optionalInt    { get; set; }
   [XmlIgnore]
   public bool optionalIntSpecified { get; set; }
   public types optionalType { get; set; }
   [XmlIgnore]
public bool optionalTypeSpecified { get; set; }
}
[Serializable]
[XmlType(Namespace="http://inquisitivechap.com/xsd")]
public enum types
{
   TypeA,
   TypeB,
   TypeC,
}
Comments (2) Trackbacks (0)
  1. Hi, thank you for the article. But it appears that optional by reference elements (minOccurs=0), such as strings, are still required in by the xml parser for .net web services. If you know of how to make string elements optional in soap requests, I would be very grateful if you drop me a note. Thank you!

    • Hi Filipp,

      So you aren’t able to generate a .net web service accepting request like that?
      Or a .net web service you are talking to doesn’t accept it (which might be the case of a contract enforcing it).


Leave a comment

No trackbacks yet.