Click here to Skip to main content
15,891,033 members
Articles / Programming Languages / XML

Yet Another XML Serialization Library for the .NET Framework

Rate me:
Please Sign up or sign in to vote.
4.92/5 (91 votes)
2 Oct 2012MIT24 min read 515.5K   207  
A flexible XML serialization library that lets developers design the XML file structure, and select the exception handling policy. YAXLib supports polymorphic serialization and serializing generic and non-generic collection classes and arrays.
<html>
<head>
<title>Yet Another XML Serialization Library for the .NET Framework</title>
</head>
<body>
<h2>Introduction</h2>

<p> In this article an XML Serialization library is presented which is referred to hereafter
    as YAXLib. .NET framework has several serialization capabilities, specially 
    <a title="MSDN: XmlSerializer class" target="_blank" href="http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx">
        XmlSerializer</a> which is widely used by developers to serialize objects to
    XML and deserialize later. My problems with the <code>XmlSerializer</code> class of the .NET
    framework were:&nbsp;</p>
<ul>
    <li>The developer is not free to choose the structure of the generated XML</li><li>
        It does not support serializing some collection classes (e.g. <code>Dictionary&lt;,&gt;</code>) 
        or properties of type <code>IEnumerable&lt;&gt;</code></li>
    <li>When deserializing it fails when some field is not present, making it unsuitable for
            storing config files, which might be edited later by human users.<br />
    </li>
</ul>

<h2>Why Use YAXLib</h2>

<p>The features of YAXLib that relief problems above, are:</p>

<ul>
    <li>The user can decide on the structure of the XML file. A property can be a child 
        element or attribute for another property or element that has no corresponding 
        property in the class.</li>
    <li>It supports serializing most of the collection types (including arrays, <code>List&lt;&gt;</code>, <code>
        HashSet&lt;&gt;</code>, <code>Dictionary&lt;,&gt;</code>, properties of type 
        <code>IEnumerable&lt;&gt;</code> and many others). For more information see the section: <a href="#issues">Known Issues / Shortcomings</a>  
        (of course the <code>XmlSerializer</code> class also supports many of these collection types).</li>
    <li>When it comes to deserialization, the developer can choose what to happen when 
        the data related to some property is not present in the XML file. This situation 
        can be regarded as error, so the library throws some exception or logs it as 
        error; or it can be regarded as warning and some predefined default value 
        specified by the developer is assigned to the absent property.</li>
    <li>The developer can choose the error-handling policy. For data sensitive 
        applications, the developer can choose that on any exceptional situation the 
        library should throw and log exceptions. For other situations (e.g. storing 
        config files in a flexible way), the developer can choose to treat exceptional 
        situations as warnings, and only log them and let the rest of the process go on.</li>
</ul>
<h2>A Tutorial of YAXLib Usage</h2>
<p>
    Let's provide a very simple example and then incrementally improve it to reflect 
    different aspects of YAXLib. To start, we define a simple <code>Warehouse</code> class:</p>
<pre lang="cs">public class Warehouse
{
    public string Name { get; set; }
    public string Address { get; set; }
    public double Area { get; set; }
}			
</pre>

<p>
    The following code snippet illustrates what is needed to serialize an object of 
    the <code>Warehouse</code> class. </p>
<pre lang="cs">// First create an instance of the Warehouse class
Warehouse w = new Warehouse()
{
    Name = &quot;Foo Warehousing Ltd.&quot;,
    Address = &quot;No. 10, Some Ave., Some City, Some Country&quot;,
    Area = 120000.50 // square meters
};

// Now serialize the instance
YAXSerializer serializer = new YAXSerializer(typeof(Warehouse));
string someString = serializer.Serialize(w);
</pre>

<p>After executing the above code, the variable <code>someS<code></code>tring</code> will contain:</p>

<pre lang="xml">&lt;Warehouse&gt; 
   &lt;Name&gt;Foo Warehousing Ltd.&lt;/Name&gt; 
   &lt;Address&gt;No. 10, Some Ave., Some City, Some Country&lt;/Address&gt; 
   &lt;Area&gt;120000.5&lt;/Area&gt; 
&lt;/Warehouse&gt;</pre>

<p>
    Note that there are other overloads of the <code>Serialize</code> method that accept 
    instances of <code>XmlWriter</code> and <code>TextWriter</code>. So far the result is much alike <code>
    XmlSerializer</code> class of the .NET Framework. Let&#39;s make some 
    modifications. We want the <code>Name</code> property to be serialized as an attribute for 
    the <code>Warehouse</code> base element, also we want that <code>Address</code> property be the <code>value</code> attribute for 
    an element named <code>Location</code>, and <code>Area</code> be the <code>value</code> attribute for an element named 
    <code>Area</code>. We modify the <code>Warehouse</code> class by adding proper attributes to its 
    properties:</p>

<pre lang="cs">
public class Warehouse
{
    [YAXAttributeForClass()]
    public string Name { get; set; }

    [YAXSerializeAs("value")]
    [YAXAttributeFor("Location")]
    public string Address { get; set; }

    [YAXSerializeAs("value")]
    [YAXAttributeFor("Area")]
    public double Area { get; set; }
}
</pre>

<p>By the above modifications our object will be serialized as:</p>

<pre lang="xml">
&lt;Warehouse Name=&quot;Foo Warehousing Ltd.&quot;&gt;
   &lt;Location value=&quot;No. 10, Some Ave., Some City, Some Country&quot; /&gt;
   &lt;Area value=&quot;120000.5&quot; /&gt;
&lt;/Warehouse&gt;</pre>

<p>Make special attention to the combination of the <code>YAXSerializeAs</code> and 
    <code>YAXAttributeFor</code> attributes for the last two properties. The <code>YAXSerializeAs</code> 
    attribute specifies a new name (alias) for the property to be shown by in the 
    XML file, but the <code>YAXAttributeFor</code> 
    attribute states that the property should be serialized as an attribute for the 
    element whose name is provided in the parameter. The corresponding attribute for creating elements is <code>YAXElementFor</code>. Note the usage of 
    <code>YAXElementFor</code> attribute for the <code>Area</code> property in the following example:</p>

<pre lang="cs">
public class Warehouse
{
    [YAXAttributeForClass()]
    public string Name { get; set; }

    [YAXSerializeAs("address")]
    [YAXAttributeFor("SiteInfo")]
    public string Address { get; set; }

    [YAXSerializeAs("SurfaceArea")]
    [YAXElementFor("SiteInfo")]
    public double Area { get; set; }
}
</pre>
<p>And the form of its XML serialization output:</p>
<pre lang="xml">
&lt;Warehouse Name="Foo Warehousing Ltd."&gt;
  &lt;SiteInfo address="No. 10, Some Ave., Some City, Some Country"&gt;
    &lt;SurfaceArea&gt;120000.5&lt;/SurfaceArea&gt;
  &lt;/SiteInfo&gt;
&lt;/Warehouse&gt;
</pre>
<p>Now let&#39;s define an enumeration of all possible items that can be stored in a 
    warehouse in general, and then specify the subset of items that is stored in our 
    hypothetical warehouse. The <code>PossibleItems</code> enum is the mentioned enumeration and 
    the <code>Items</code> array is the mentioned subset as defined in the following code 
    snippet:</p>
<pre lang="cs">
public enum PossibleItems
{
    Item1, Item2, Item3, Item4, Item5, Item6,
    Item7, Item8, Item9, Item10, Item11, Item12
}

public class Warehouse
{
    [YAXAttributeForClass()]
    public string Name { get; set; }

    [YAXSerializeAs("address")]
    [YAXAttributeFor("SiteInfo")]
    public string Address { get; set; }

    [YAXSerializeAs("SurfaceArea")]
    [YAXElementFor("SiteInfo")]
    public double Area { get; set; }

    public PossibleItems[] Items { get; set; }
}
</pre>

<p>We modify the instance creation code as following, while the serialization code 
    remains intact as before:</p>
<pre lang="cs">
Warehouse w = new Warehouse()
{
    Name = "Foo Warehousing Ltd.",
    Address = "No. 10, Some Ave., Some City, Some Country",
    Area = 120000.50, // square meters
    Items = new PossibleItems[] { PossibleItems.Item3, PossibleItems.Item6, PossibleItems.Item9, PossibleItems.Item12 }
};
</pre>

<p>
    The XML generated by serialization is:</p>
<pre lang="xml">
&lt;Warehouse Name="Foo Warehousing Ltd."&gt;
  &lt;SiteInfo address="No. 10, Some Ave., Some City, Some Country"&gt;
    &lt;SurfaceArea&gt;120000.5&lt;/SurfaceArea&gt;
  &lt;/SiteInfo&gt;
  &lt;Items&gt;
    &lt;PossibleItems&gt;Item3&lt;/PossibleItems&gt;
    &lt;PossibleItems&gt;Item6&lt;/PossibleItems&gt;
    &lt;PossibleItems&gt;Item9&lt;/PossibleItems&gt;
    &lt;PossibleItems&gt;Item12&lt;/PossibleItems&gt;
  &lt;/Items&gt;
&lt;/Warehouse&gt;
</pre>

<p>
    But we are not satisfied with the result, so let&#39;s change the <code>Items</code> element name 
    to <code>StoreableItems</code> and change each child element name from <code>PossibleItems</code> simply 
    to <code>Item</code>. Here&#39;s the modifications made to the <code>Warehouse</code> class:</p>
<pre lang="cs">
public class Warehouse
{
    [YAXAttributeForClass()]
    public string Name { get; set; }

    [YAXSerializeAs("address")]
    [YAXAttributeFor("SiteInfo")]
    public string Address { get; set; }

    [YAXSerializeAs("SurfaceArea")]
    [YAXElementFor("SiteInfo")]
    public double Area { get; set; }

    [YAXCollection(YAXCollectionSerializationTypes.Recursive, EachElementName="Item")]
    [YAXSerializeAs("StoreableItems")]
    public PossibleItems[] Items { get; set; }
}
</pre>
<p>
    And here&#39;s the XML output:</p>
<pre lang="xml">
&lt;Warehouse Name="Foo Warehousing Ltd."&gt;
  &lt;SiteInfo address="No. 10, Some Ave., Some City, Some Country"&gt;
    &lt;SurfaceArea&gt;120000.5&lt;/SurfaceArea&gt;
  &lt;/SiteInfo&gt;
  &lt;StoreableItems&gt;
    &lt;Item&gt;Item3&lt;/Item&gt;
    &lt;Item&gt;Item6&lt;/Item&gt;
    &lt;Item&gt;Item9&lt;/Item&gt;
    &lt;Item&gt;Item12&lt;/Item&gt;
  &lt;/StoreableItems&gt;
&lt;/Warehouse&gt;
</pre>
<p>
    How about serializing the array as a comma seperated list of items? Modify the 
    <code>Warehouse</code> class as:</p>

<pre lang="cs">
public class Warehouse
{
    [YAXAttributeForClass()]
    public string Name { get; set; }

    [YAXSerializeAs("address")]
    [YAXAttributeFor("SiteInfo")]
    public string Address { get; set; }

    [YAXSerializeAs("SurfaceArea")]
    [YAXElementFor("SiteInfo")]
    public double Area { get; set; }

    [YAXCollection(YAXCollectionSerializationTypes.Serially, SeparateBy=", ")]
    [YAXSerializeAs("StoreableItems")]
    public PossibleItems[] Items { get; set; }
}
</pre>

<p>
    and view the corresponding XML output:</p>
    
<pre lang="xml">
&lt;Warehouse Name="Foo Warehousing Ltd."&gt;
  &lt;SiteInfo address="No. 10, Some Ave., Some City, Some Country"&gt;
    &lt;SurfaceArea&gt;120000.5&lt;/SurfaceArea&gt;
  &lt;/SiteInfo&gt;
  &lt;StoreableItems&gt;Item3, Item6, Item9, Item12&lt;/StoreableItems&gt;
&lt;/Warehouse&gt;
</pre>

<p>
    The latter two examples need more explanation of the <code>YAXCollection</code> attribute. 
    The first parameter passed to this attribute is the type of the serialization of 
    the collection classes, which there are two of them defined in the library. One is called <code>Recursive</code> and the other is called
    <code>Serially</code>. By <code>Recursive</code> we mean that every member of the collection object is serialized as a child element 
    of the collection itself. If <code>Recursive</code> is selected, then the developer may choose to rename each child element name
    by providing the <code>EachElementName</code> named parameter of the attribute. The second type of serialization of 
    the collection classes is called <code>Serially</code>, and by which we mean that child elements of the collection object
    are serialized in only one element all seperated by some delimiter specified by the <code>SeparateBy</code> named parameter.</p>

<p>
    Now let&#39;s improve our example a little more by adding a dictionary of, say, 
    <code>PossibleItems</code>, and their quantity, as stored in our hypothetical warehouse.</p>

<pre lang="cs">
public class Warehouse
{
    [YAXAttributeForClass()]
    public string Name { get; set; }

    [YAXSerializeAs("address")]
    [YAXAttributeFor("SiteInfo")]
    public string Address { get; set; }

    [YAXSerializeAs("SurfaceArea")]
    [YAXElementFor("SiteInfo")]
    public double Area { get; set; }

    [YAXCollection(YAXCollectionSerializationTypes.Serially, SeparateBy=", ")]
    [YAXSerializeAs("StoreableItems")]
    public PossibleItems[] Items { get; set; }

    public Dictionary&lt;PossibleItems, int&gt; ItemQuantitiesDic { get; set; }
}
</pre>

<p>
    We instantiate an object as follows:</p>

<pre lang="cs">
Dictionary&lt;PossibleItems,int&gt; dicItems = new Dictionary&lt;PossibleItems,int&gt;();
dicItems.Add(PossibleItems.Item3, 10);
dicItems.Add(PossibleItems.Item6, 120);
dicItems.Add(PossibleItems.Item9, 600);
dicItems.Add(PossibleItems.Item12, 25);

Warehouse w = new Warehouse()
{
    Name = "Foo Warehousing Ltd.",
    Address = "No. 10, Some Ave., Some City, Some Country",
    Area = 120000.50, // square meters
    Items = new PossibleItems[] { PossibleItems.Item3, PossibleItems.Item6, PossibleItems.Item9, PossibleItems.Item12 },
    ItemQuantitiesDic = dicItems
};
</pre>

<p>
    And here&#39;s the XML output:</p>

<pre lang="xml">
&lt;Warehouse Name="Foo Warehousing Ltd."&gt;
  &lt;SiteInfo address="No. 10, Some Ave., Some City, Some Country"&gt;
    &lt;SurfaceArea&gt;120000.5&lt;/SurfaceArea&gt;
  &lt;/SiteInfo&gt;
  &lt;StoreableItems&gt;Item3, Item6, Item9, Item12&lt;/StoreableItems&gt;
  &lt;ItemQuantitiesDic&gt;
    &lt;KeyValuePairOfPossibleItemsInt32&gt;
      &lt;Key&gt;Item3&lt;/Key&gt;
      &lt;Value&gt;10&lt;/Value&gt;
    &lt;/KeyValuePairOfPossibleItemsInt32&gt;
    &lt;KeyValuePairOfPossibleItemsInt32&gt;
      &lt;Key&gt;Item6&lt;/Key&gt;
      &lt;Value&gt;120&lt;/Value&gt;
    &lt;/KeyValuePairOfPossibleItemsInt32&gt;
    &lt;KeyValuePairOfPossibleItemsInt32&gt;
      &lt;Key&gt;Item9&lt;/Key&gt;
      &lt;Value&gt;600&lt;/Value&gt;
    &lt;/KeyValuePairOfPossibleItemsInt32&gt;
    &lt;KeyValuePairOfPossibleItemsInt32&gt;
      &lt;Key&gt;Item12&lt;/Key&gt;
      &lt;Value&gt;25&lt;/Value&gt;
    &lt;/KeyValuePairOfPossibleItemsInt32&gt;
  &lt;/ItemQuantitiesDic&gt;
&lt;/Warehouse&gt;
</pre>

<p>
    Yet we can improve the output by renaming the incomprehensible element names, 
    e.g. renaming <code>ItemQuantitiesDic</code> to simply <code>ItemQuantities</code> and <code>KeyValuePairOfPossibleItemsInt32</code> 
    (which is the friendly name of <code>KeyValuePair&lt;PossibleItems, Int32&gt;</code>) to, say, 
    <code>ItemInfo</code>. So we add proper attributes to the definition of <code>ItemQuantitiesDic</code> as 
    follows:</p>
    
<pre lang="cs">
public class Warehouse
{
    [YAXAttributeForClass()]
    public string Name { get; set; }

    [YAXSerializeAs("address")]
    [YAXAttributeFor("SiteInfo")]
    public string Address { get; set; }

    [YAXSerializeAs("SurfaceArea")]
    [YAXElementFor("SiteInfo")]
    public double Area { get; set; }

    [YAXCollection(YAXCollectionSerializationTypes.Serially, SeparateBy=", ")]
    [YAXSerializeAs("StoreableItems")]
    public PossibleItems[] Items { get; set; }

    [YAXCollection(YAXCollectionSerializationTypes.Recursive, EachElementName="ItemInfo")]
    [YAXSerializeAs("ItemQuantities")]
    public Dictionary&lt;PossibleItems, int&gt; ItemQuantitiesDic { get; set; }
}
</pre>

<p>
    And here&#39;s the XML output:</p>

<pre lang="xml">
&lt;Warehouse Name="Foo Warehousing Ltd."&gt;
  &lt;SiteInfo address="No. 10, Some Ave., Some City, Some Country"&gt;
    &lt;SurfaceArea&gt;120000.5&lt;/SurfaceArea&gt;
  &lt;/SiteInfo&gt;
  &lt;StoreableItems&gt;Item3, Item6, Item9, Item12&lt;/StoreableItems&gt;
  &lt;ItemQuantities&gt;
    &lt;ItemInfo&gt;
      &lt;Key&gt;Item3&lt;/Key&gt;
      &lt;Value&gt;10&lt;/Value&gt;
    &lt;/ItemInfo&gt;
    &lt;ItemInfo&gt;
      &lt;Key&gt;Item6&lt;/Key&gt;
      &lt;Value&gt;120&lt;/Value&gt;
    &lt;/ItemInfo&gt;
    &lt;ItemInfo&gt;
      &lt;Key&gt;Item9&lt;/Key&gt;
      &lt;Value&gt;600&lt;/Value&gt;
    &lt;/ItemInfo&gt;
    &lt;ItemInfo&gt;
      &lt;Key&gt;Item12&lt;/Key&gt;
      &lt;Value&gt;25&lt;/Value&gt;
    &lt;/ItemInfo&gt;
  &lt;/ItemQuantities&gt;
&lt;/Warehouse&gt;
</pre>

<p>
    One question that arises at the time is that, is it possible to rename <code>Key</code> or <code>Value</code> 
    elements or reformat the parent <code>ItemInfo</code> element? The answer currently is no, because, 
    these are instances of the <code>KeyValuePair&lt;,&gt;</code> class that are serialized, and we do 
    not have access to the definition of this class to add formatting attributes to its 
    properties. If one needs special formatting of the output, he/she has to define 
    his/her custom classes (with proper formatting attributes) and use them instead of 
    <code>KeyValuePair&lt;,&gt;</code> in some collection class (e.g. <code>List&lt;MyCustomItemHolder&gt;</code>).</p>
<p>
    Another question is that, is it possible to serialize members of our dictionary 
    serially? Again the answer is no. Only collections of basic data types (which 
    are 
    primitive data types, enums, string, and the <code>DateTime</code> structure) 
    can be serialized serially with no overhead for the developer (More precisely speaking, everything can be serialized serially, 
    but they cannot be <i>deserialized</i> if they are <i>not</i> among basic data 
    types).</p>
<p>
        As the last improvement let&#39;s see how nested objects are serialized. We define 
    a <code>Person</code> class and add an <code>Owner</code> property of type <code>Person</code> to our hypothetical 
    warehouse.</p>
<pre lang="cs">
public class Person
{
    public string SSN { get; set; }
    public string Name { get; set; }
    public string Family { get; set; }
    public int Age { get; set; }
}

public class Warehouse
{
    [YAXAttributeForClass()]
    public string Name { get; set; }

    [YAXSerializeAs("address")]
    [YAXAttributeFor("SiteInfo")]
    public string Address { get; set; }

    [YAXSerializeAs("SurfaceArea")]
    [YAXElementFor("SiteInfo")]
    public double Area { get; set; }

    [YAXCollection(YAXCollectionSerializationTypes.Serially, SeparateBy=", ")]
    [YAXSerializeAs("StoreableItems")]
    public PossibleItems[] Items { get; set; }

    [YAXCollection(YAXCollectionSerializationTypes.Recursive, EachElementName="ItemInfo")]
    [YAXSerializeAs("ItemQuantities")]
    public Dictionary&lt;PossibleItems, int&gt; ItemQuantitiesDic { get; set; }

    public Person Owner { get; set; }
}
</pre>
    
<p>And create an instance as follows:</p>

<pre lang="cs">
Dictionary&lt;PossibleItems,int&gt; dicItems = new Dictionary&lt;PossibleItems,int&gt;();
dicItems.Add(PossibleItems.Item3, 10);
dicItems.Add(PossibleItems.Item6, 120);
dicItems.Add(PossibleItems.Item9, 600);
dicItems.Add(PossibleItems.Item12, 25);

Warehouse w = new Warehouse()
{
    Name = "Foo Warehousing Ltd.",
    Address = "No. 10, Some Ave., Some City, Some Country",
    Area = 120000.50, // square meters
    Items = new PossibleItems[] { PossibleItems.Item3, PossibleItems.Item6, PossibleItems.Item9, PossibleItems.Item12 },
    ItemQuantitiesDic = dicItems,
    Owner = new Person() { SSN = "123456789", Name = "John", Family = "Doe", Age = 50 }
};
</pre>
    
<p>
    Here is the XML output:</p>
    
<pre lang="xml">
&lt;Warehouse Name="Foo Warehousing Ltd."&gt;
  &lt;SiteInfo address="No. 10, Some Ave., Some City, Some Country"&gt;
    &lt;SurfaceArea&gt;120000.5&lt;/SurfaceArea&gt;
  &lt;/SiteInfo&gt;
  &lt;StoreableItems&gt;Item3, Item6, Item9, Item12&lt;/StoreableItems&gt;
  &lt;ItemQuantities&gt;
    &lt;ItemInfo&gt;
      &lt;Key&gt;Item3&lt;/Key&gt;
      &lt;Value&gt;10&lt;/Value&gt;
    &lt;/ItemInfo&gt;
    &lt;ItemInfo&gt;
      &lt;Key&gt;Item6&lt;/Key&gt;
      &lt;Value&gt;120&lt;/Value&gt;
    &lt;/ItemInfo&gt;
    &lt;ItemInfo&gt;
      &lt;Key&gt;Item9&lt;/Key&gt;
      &lt;Value&gt;600&lt;/Value&gt;
    &lt;/ItemInfo&gt;
    &lt;ItemInfo&gt;
      &lt;Key&gt;Item12&lt;/Key&gt;
      &lt;Value&gt;25&lt;/Value&gt;
    &lt;/ItemInfo&gt;
  &lt;/ItemQuantities&gt;
  &lt;Owner&gt;
    &lt;SSN&gt;123456789&lt;/SSN&gt;
    &lt;Name&gt;John&lt;/Name&gt;
    &lt;Family&gt;Doe&lt;/Family&gt;
    &lt;Age&gt;50&lt;/Age&gt;
  &lt;/Owner&gt;
&lt;/Warehouse&gt;</pre>

<p>
    Again we add more formatting attributes to both of the classes. For example we 
    change the <code>Person</code> class as follows:&nbsp; </p>

<pre lang="cs">
public class Person
{
    [YAXAttributeForClass()]
    public string SSN { get; set; }

    [YAXAttributeFor("Identification")]
    public string Name { get; set; }

    [YAXAttributeFor("Identification")]
    public string Family { get; set; }
    
    public int Age { get; set; }
}
</pre>

<p>
    The above modification produces the following output:</p>
<pre lang="xml">
&lt;Warehouse Name="Foo Warehousing Ltd."&gt;
  &lt;SiteInfo address="No. 10, Some Ave., Some City, Some Country"&gt;
    &lt;SurfaceArea&gt;120000.5&lt;/SurfaceArea&gt;
  &lt;/SiteInfo&gt;
  &lt;StoreableItems&gt;Item3, Item6, Item9, Item12&lt;/StoreableItems&gt;
  &lt;ItemQuantities&gt;
    &lt;ItemInfo&gt;
      &lt;Key&gt;Item3&lt;/Key&gt;
      &lt;Value&gt;10&lt;/Value&gt;
    &lt;/ItemInfo&gt;
    &lt;ItemInfo&gt;
      &lt;Key&gt;Item6&lt;/Key&gt;
      &lt;Value&gt;120&lt;/Value&gt;
    &lt;/ItemInfo&gt;
    &lt;ItemInfo&gt;
      &lt;Key&gt;Item9&lt;/Key&gt;
      &lt;Value&gt;600&lt;/Value&gt;
    &lt;/ItemInfo&gt;
    &lt;ItemInfo&gt;
      &lt;Key&gt;Item12&lt;/Key&gt;
      &lt;Value&gt;25&lt;/Value&gt;
    &lt;/ItemInfo&gt;
  &lt;/ItemQuantities&gt;
  &lt;Owner SSN="123456789"&gt;
    &lt;Identification Name="John" Family="Doe" /&gt;
    &lt;Age&gt;50&lt;/Age&gt;
  &lt;/Owner&gt;
&lt;/Warehouse&gt;
</pre>

<h2>
    Deserialization and Error Handling Schemes
</h2>

<p>
    Assume that we successfully serialized an object of our hypothetical warehouse 
    into XML, and stored it into some file. In order to deserialize the XML back into 
    an object, use the <code>Deserialize</code> method in the following style:</p>

<pre lang="cs">
YAXSerializer ser = new YAXSerializer(typeof(Warehouse));
try
{
    object o = ser.Deserialize(someStringContainingXML);
    if (o != null)
        // means that the XML input has been deserialized successfully
    else
        // the XML input cannot be deserialized successfully
}
catch
{
}
</pre>

<p>
    It is generally inconvenient to have the content of the XML stored in some 
    string variable as in the example above. For this reason there are overloads of the 
    <code>Deserialize</code> method that accept as parameter instances of <code>XmlReader</code> or <code>TextReader</code>. 
    Also there is another method called <code>DeserializeFromFile</code> which accepts as parameter 
    the path to the XML input file.</p>
<p>
    No matter which method is used, always use the above style 
    for deserialization, i.e. put the deserialization method within a <code>try</code> block, and 
    always compare the result with <code>null</code>.</p>

<p>
    The developer cannot assume that the deserialization process always succeeds 
	especially if the XML file 
    has been edited by a human user. The problems that may occur are mal-formed XML 
    file, missing elements or attributes for which a property exists, and several others. To 
    get a better view of different problems which might happen in YAXLib see the 
    section: <a href="#exceptions">YAXLib Exception Classes</a>. To give the 
    developers more control on handling of the errors there are some extra features 
    added to YAXLib.</p>
<p>
    One feature is the <code>ParsingErrors</code> property of the <code>YAXSerializer</code> 
    class. This property is a reference to a collection of <code>YAXException</code>s that 
	has happened 
    during deserialization or serialization but has not been thrown. Actually this 
    collection serves as the logger of the library (we later discuss what is meant 
    by not thrown exceptions). The developer may choose later to loop 
    through the exceptions, or simply call the overloaded <code>ToString()</code> method to print 
    the list of exceptions in a neat format (this is what is done in the demo 
    application).</p>
<p>
    Another feature is enabling the developers to choose the exception handling policy of 
    the library. This is done by providing the <code>YAXExceptionHandlingPolicies</code> parameter of the
    constructor of the <code>YAXSerializer</code>. This is an <code>enum</code> with three possible values:</p>

<ul>
    <li><code>ThrowWarningsAndErrors</code>: throws an exception at the time that any 
        problem 
        (labeled as warning or error) occurs. 
        In this case the deserialization process cannot continue and the operation 
        fails. This is the default policy if not specified otherwise by the developer.</li>
    <li><code>ThrowErrorsOnly</code>: only throws exceptions corresponding to those 
        labeled as error, and only logs those that are labeled as warning which might be 
        accessed later by the <code>ParsingErrors</code> property.</li>
    <li><code>DoNotThrow</code>: logs all kinds of exceptions (either labeled as warning 
        or error) and do not throw any of them. The <code>YAXCannotSerializeSelfReferentialTypes</code> 
        and <code>YAXObjectTypeMismatch</code> are two exceptions of this rule, but note 
        that both of these two exception classes happen only at serialization time, not 
        during deserialization.</li>
</ul>
<p>
    The 3rd parameter of the constructor of the <code>YAXSerializer</code> class 
	specifies the default exception type (warning or error) for all exceptions that may happen 
    (the default is <code>Error</code>). One can alter this behavior for a special 
    property of the class he/she wants to serialize by using the <code>YAXErrorIfMissed</code> 
    attribute. This attribute, also lets the developer to pick a default value for the 
    property, which is used if the data corresponding to that property is missed 
    in the input XML. As an example consider the <code>ProgrammingLanguage</code> class below.</p>

<pre lang="cs">
public class ProgrammingLanguage
{
    public string LanguageName { get; set; }
    public bool IsCaseSensitive { get; set; }
}
</pre>

<p>
    We require that <code>LanguageName</code> property be mandatory, but <code>IsCaseSensitive</code> property 
    be optional. If the value corresponding to <code>IsCaseSensitive</code> property is missed in 
    the XML, then we consider it as <code>true</code>. For this purpose we add 
    attributes to the class definition as shown below:</p>
<pre lang="cs">
public class ProgrammingLanguage
{
    [YAXErrorIfMissed(YAXExceptionTypes.Error)]
    public string LanguageName { get; set; }

    [YAXErrorIfMissed(YAXExceptionTypes.Warning, DefaultValue=true)]
    public bool IsCaseSensitive { get; set; }
}
</pre>

<p>
    As seen in the above code snippet, we treat absence of <code>LanguageName</code> as an error, 
    but we treat absence of <code>IsCaseSensitive</code> as a warning, and if this happens we 
    assign the default value of <code>true</code> to this property. Consequently we use the 
    <code>ThrowErrorsOnly</code> exception handling policy and treat all other exceptions as 
    warnings. The code below illustrates this while assumes that the input XML is 
    stored in the string variable <code>inputXML</code>.</p>
    
<pre lang="cs">
YAXSerializer serializer = new YAXSerializer(typeof(ProgrammingLanguage), 
    YAXExceptionHandlingPolicies.ThrowErrorsOnly, 
    YAXExceptionTypes.Warning);
object deserializedObject = null;
try
{
    deserializedObject = serializer.Deserialize(inputXML);

    if (serializer.ParsingErrors.ContainsAnyError)
    {
        Console.WriteLine("Succeeded to deserialize, but these problems also happened:");
        Console.WriteLine(serializer.ParsingErrors.ToString());
    }
}
catch (YAXException ex)
{
    Console.WriteLine("Failed to deserialize input, this is the error occurred:");
    Console.WriteLine(ex.ToString());
}
</pre>
<p>
    If the input XML is in the following format, then the deserialization finishes 
    successfully:</p>
    
<pre lang="xml">
&lt;ProgrammingLanguage&gt;
  &lt;LanguageName&gt;C#&lt;/LanguageName&gt;
  &lt;IsCaseSensitive&gt;true&lt;/IsCaseSensitive&gt;
&lt;/ProgrammingLanguage&gt;
</pre>

<p>
    The following XML input, leads to a warning which will be stored in the 
    <code>ParsingErrors</code> collection, but the value of <code>IsCaseSensitive</code> will be set to <code>true</code>.</p>
    
<pre lang="xml">
&lt;ProgrammingLanguage&gt;
  &lt;LanguageName&gt;C#&lt;/LanguageName&gt;
  &lt;!-- &lt;IsCaseSensitive&gt;true&lt;/IsCaseSensitive&gt; --&gt;
&lt;/ProgrammingLanguage&gt;
</pre>
<p>
    But the following XML input, leads to an exception being thrown, and therefore 
    the deserialization process fails.</p>

<pre lang="xml">
&lt;ProgrammingLanguage&gt;
  &lt;!-- &lt;LanguageName&gt;C#&lt;/LanguageName&gt; --&gt;
  &lt;IsCaseSensitive&gt;true&lt;/IsCaseSensitive&gt;
&lt;/ProgrammingLanguage&gt;
</pre>
<h2>
    The Problem with Self-Referential Classes</h2>
<p>
    Self-referential classes are those that have a property of the same type as the 
    class itself. Self-referential classes cannot be serialized, because they lead 
    to infinite recursion, and hence stack-overflow. The 
    <code>YAXCannotSerializeSelfReferentialTypes</code> exception is thrown if such classes are 
    encountered during the serialization process. This is the only YAXLib exception 
    that cannot be turned off even if the exception-handling policy is set to 
    <code>DoNotThrow</code>. So it is recommended that you always put your serialization code 
    within <code>try</code> blocks.</p>
<p>
    As an example the <code>System.Drawing.Color</code> struct of the .NET Framework is 
    self-referential (because it has a public property of type <code>Color</code>, and YAXLib is 
    only interested in properties). To overcome this problem one can create 
    properties of some other types (e.g. <code>string</code>) in which the <code>get</code> and <code>set</code> methods 
    convert between the two types (e.g. convert <code>string</code> to <code>Color</code> and vice versa). 
    Here&#39;s an example that uses a property of type <code>string</code> instead of a property of 
    type <code>Color</code>:</p>
    
<pre lang="cs">
public class SelfReferentialExample
{
    private Color m_color = Color.Blue;

    public string TheColor
    {
        get
        {
            return String.Format("#{0:X}", m_color.ToArgb());
        }

        set
        {
            m_color = Color.White;

            value = value.Trim();
            if (value.StartsWith("#")) // remove leading # if any
                value = value.Substring(1);

            int n;
            if (Int32.TryParse(value, System.Globalization.NumberStyles.HexNumber, null, out n))
            {
                m_color = Color.FromArgb(n);
            }
        }
    }
}
</pre>

<p>Here is the XML generated by serializing an object of the above class:</p>

<pre lang="xml">
&lt;SelfReferentialExample&gt;
  &lt;TheColor&gt;#FF0000FF&lt;/TheColor&gt;
&lt;/SelfReferentialExample&gt;
</pre>

<h2>YAXLib Attributes</h2>
<p>
    In this section we list and describe all attribute classes provided in the 
    YAXLib library.
</p>
<ol>
    <li><b><code>YAXSerializeAs</code></b>: Defines an alias for the property 
        or class under which it will be serialized. This attribute is applicable to 
        properties and classes only.</li>
    <li><b><code>YAXDontSerialize</code></b>: Prevents serialization of some property. 
        This attribute is applicable to properties only.</li>
    <li><b><code>YAXAttributeForClass</code></b>: Makes a property to appear as an 
        attribute for the class (i.e. the parent element) if possible. This attribute is 
        applicable to properties only.</li>
    <li><b><code>YAXAttributeFor</code></b>: Makes a property to appear 
        as an attribute for another element, if possible. This attribute is applicable 
        to properties only.</li>
    <li><b><code>YAXElementFor</code></b>: Makes a property to appear as 
        a child element for another element. This attribute is applicable to properties 
        only.</li>
    <li><b><code>YAXCollection</code></b>: Controls the serialization of 
        collection instances. This attribute is applicable to properties only. For more 
        details and examples see section: A Tutorial of YAXLib Usage</li>
    <li><b><code>YAXErrorIfMissed</code></b>: Specifies the behavior of 
        the deserialization method, if the element/attribute corresponding to this 
        property is missed in the XML input. This attribute is applicable to properties 
        only.</li>
</ol>

<h2><a name="exceptions"></a>YAXLib Exception Classes</h2>

<p>
    In this section we list and describe all exception classes which might be thrown 
    or logged as error, by YAXLib. Note that all the exception classes in YAXLib are 
    derived from <code>YAXException</code> class.</p>
<ul>
    <li>Exceptions raised during serialization<ol>
        <li><b><code>YAXAttributeAlreadyExistsException</code></b>: Raised when trying to serialize an attribute 
            where another attribute with the same name already exists.</li>
        <li><b><code>YAXCannotSerializeSelfReferentialTypes</code></b>: Raised when trying to serialize 
            self-referential types. This exception cannot be turned off.</li>
        <li><b><code>YAXObjectTypeMismatch</code></b>: Raised when the object provided for serialization is not 
            of the type provided for the serializer. This exception cannot be turned off.</li>
        </ol>
    </li>
    <li>Exceptions raised during deserialization<ol>
        <li><b><code>YAXAttributeMissingException</code></b>: Raised when the attribute corresponding to some 
            property is not present in the given XML file.</li>
        <li><b><code>YAXElementMissingException</code></b>: Raised when the element corresponding to some 
            property is not present in the given XML file.</li>
        <li><b><code>YAXBadlyFormedInput</code></b>: Raised when the value provided for some property in the XML 
            input, cannot be converted to the type of the property.</li>
        <li><b><code>YAXPropertyCannotBeAssignedTo</code></b>: Raised when the value provided for some property 
            in the XML input, cannot be assigned to the property.</li>
        <li><b><code>YAXCannotAddObjectToCollection</code></b>: Raised when some member of the collection in the 
            input XML, cannot be added to the collection object.</li>
        <li><b><code>YAXDefaultValueCannotBeAssigned</code></b>: Raised when the default value specified by the <code>
            YAXErrorIfMissedAttribute</code> could not be assigned to the property.</li>
        <li><b><code>YAXBadlyFormedXML</code></b>: Raised when the XML input does not follow standard XML 
            formatting rules.</li>
        </ol>
    </li>
</ul>

<h2>When Not to Use YAXLib</h2>

<p>
    The emphasis of YAXLib on the formatting of the generated XML output and ignoring 
    the deserialization errors as far as possible (of course as specified by the 
    developer), shows that it is more suitable for communication between program and 
    human users. If your sole purpose is just to store the internal status of some 
    object to be loaded later, and it is <i>not</i> going to be edited by a human user 
    later, and your objects do not use collection classes such as <code>Dictionary&lt;,&gt;</code>; 
	then it might 
    be better to use the <code>XmlSerializer</code> class of the .NET Framework.</p>

<p>
    YAXLib assumes that the public properties of the objects, are all that is needed 
    to be serialized. <a target="_blank" href="http://www.codeproject.com/KB/XML/XmlSerializer.aspx">
    Here</a> you can find another work that takes into account private fields also.</p>

<h2><a name="issues"></a>Known Issues / Shortcomings</h2>

<p>
    Try to use YAXLib with generic collection types only (e.g. use <code>Dictionary&lt;,&gt;</code> 
    instead of <code>Hashtable</code>). </p>
<p>
    The behavior of the library is undefined when it is 
    working on non-generic collections of heterogeneous members (e.g. a non-generic 
    <code>ArrayList</code> that contains integers and strings and other type of 
	objects at the same 
    time).</p>
<p>
    YAXLib is not able to define alias for <code>enum</code> items (maybe a future work).</p>
<h2>History</h2>

<ul>
    <li>&nbsp;<b>March 13, 2009</b>: 1st version.</li>
</ul>
</body>
</html>

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Software Developer
Australia Australia
A software designer and developer

Comments and Discussions