Click here to Skip to main content
Click here to Skip to main content

Yet Another XML Serialization Library for the .NET Framework

, 2 Oct 2012
Rate this:
Please Sign up or sign in to vote.
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.

Contents

Introduction

In this article, an XML serialization library is presented which is referred to hereafter as YAXLib. The .NET Framework has several serialization capabilities, specially XmlSerializer, which is widely used by developers to serialize objects to XML and deserialize later. My problems with the XmlSerializer class of the .NET framework were:

  • The developer is not free to choose the structure of the generated XML.
  • It does not support serializing some collection classes (e.g., Dictionary<,>) or properties of type IEnumerable<>.
  • When deserializing, it fails when some fields are not present, making it unsuitable for storing configuration files, which might be edited later by human users.

Why Use YAXLib

The features of YAXLib that solve the above problems are:

  • The user can decide on the structure of the XML file. A property can be a child element, or attribute for another property, or an element that has no corresponding property in the class.
  • The collection classes can be serialized as a comma-separated (or any other separator) list of data items. Also, there are special formatting capabilities designed for objects of type Dictionary<,>, so that the developer has full control on the appearance of the generated XML.
  • It supports serialization and deserialization of all the generic collection classes within the System.Collections.Generic namespace (namely Dictionary, HashSet, LinkedList, List, Queue, SortedDictionary, SortedList, and Stack) and all the non-generic collection classes within the System.Collections namespace (namely ArrayList, BitArray, Hashtable, Queue, SortedList, and Stack). The non-generic collection classes could contain objects of heterogeneous types. Also, it supports serialization and deserialization of single-dimensional, multi-dimensional, and jagged arrays.
  • It supports serializing and deserializing objects and collections of items, through a reference to their base class, or interface.
  • It supports multi-stage deserialization.
  • The developer can provide XML comments within the XML output.
  • When it comes to deserialization, the developer can choose what happens when data related to some property is not present in the XML file. This situation can be regarded as an error so the library throws some exception or logs it as an error, or it can be regarded as a warning and some predefined default value specified by the developer is assigned to the absent property. Also, the developer may choose to ignore this problem, so that the related exception is neither thrown nor logged. See the section Preserving Null-References Identity to see when ignoring absent data might be useful.
  • 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.

A Tutorial of YAXLib Usage

Basic Usage and Formatting

Let's see a very simple example, and then incrementally improve it to reflect different aspects of YAXLib. To start, we define a simple Warehouse class:

public class Warehouse
{
    public string Name { get; set; }
    public string Address { get; set; }
    public double Area { get; set; }
}

The following code snippet illustrates what is needed to serialize an object of the Warehouse class:

// First create an instance of the Warehouse class
Warehouse w = new Warehouse()
{
    Name = "Foo Warehousing Ltd.",
    Address = "No. 10, Some Ave., Some City, Some Country",
    Area = 120000.50 // square meters
};

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

After executing the above code, the variable someString will contain:

<Warehouse>
   <Name>Foo Warehousing Ltd.</Name>
   <Address>No. 10, Some Ave., Some City, Some Country</Address>
   <Area>120000.5</Area>
</Warehouse>

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

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; }
}

With the above modifications, our object will be serialized as:

<Warehouse Name="Foo Warehousing Ltd.">
   <Location value="No. 10, Some Ave., Some City, Some Country" />
   <Area value="120000.5" />
</Warehouse>

Pay special attention to the combination of the YAXSerializeAs and YAXAttributeFor attributes for the last two properties. The YAXSerializeAs attribute specifies a new name (alias) for the property to be shown in the XML file, but the YAXAttributeFor 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 YAXElementFor. Note the usage of the YAXElementFor attribute for the Area property in the following example:

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; }
}

And, the form of its XML serialization output:

<Warehouse Name="Foo Warehousing Ltd.">
  <SiteInfo address="No. 10, Some Ave., Some City, Some Country">
    <SurfaceArea>120000.5</SurfaceArea>
  </SiteInfo>
</Warehouse>

Serializing Objects of Collection Classes

Now, let'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 PossibleItems enum is the mentioned enumeration, and the Items array is the mentioned subset, as defined in the following code snippet:

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; }
}

We modify the instance creation code as follows, while the serialization code remains intact as before:

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 }
};

The XML generated by the serialization is:

<Warehouse Name="Foo Warehousing Ltd.">
  <SiteInfo address="No. 10, Some Ave., Some City, Some Country">
    <SurfaceArea>120000.5</SurfaceArea>
  </SiteInfo>
  <Items>
    <PossibleItems>Item3</PossibleItems>
    <PossibleItems>Item6</PossibleItems>
    <PossibleItems>Item9</PossibleItems>
    <PossibleItems>Item12</PossibleItems>
  </Items>
</Warehouse>

But, we are not satisfied with the result, so let's change the Items element name to StoreableItems, and change each child element name from PossibleItems to Item. Here are the modifications made to the Warehouse class:

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; }
}

And, here's the XML output:

<Warehouse Name="Foo Warehousing Ltd.">
  <SiteInfo address="No. 10, Some Ave., Some City, Some Country">
    <SurfaceArea>120000.5</SurfaceArea>
  </SiteInfo>
  <StoreableItems>
    <Item>Item3</Item>
    <Item>Item6</Item>
    <Item>Item9</Item>
    <Item>Item12</Item>
  </StoreableItems>
</Warehouse>

How about serializing the array as a comma separated list of items? Modify the Warehouse class as:

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; }
}

and view the corresponding XML output:

<Warehouse Name="Foo Warehousing Ltd.">
  <SiteInfo address="No. 10, Some Ave., Some City, Some Country">
    <SurfaceArea>120000.5</SurfaceArea>
  </SiteInfo>
  <StoreableItems>Item3, Item6, Item9, Item12</StoreableItems>
</Warehouse>

The latter two examples need more explanation of the YAXCollection attribute. The first parameter passed to this attribute is the type of serialization of the collection classes, and there are three of them defined in the library: one is called Recursive, another is RecursiveWithNoContainingElement, and the third one is Serially. By Recursive, we mean that every member of the collection object is serialized as a child element of the collection itself. If Recursive is selected, then the developer may choose to rename each child element name by providing the EachElementName named parameter of the attribute. The second type of serialization of the collection classes is RecursiveWithNoContainingElement, which operates just like Recursive except that the collection itself does not have a separate element for itself. All the collection items will be serialized as if they are members of the containing class. Finally, the third type of serialization of the collection classes is called Serially, and by which we mean that child elements of the collection object are serialized in only one element, all separated by some delimiter specified by the SeparateBy named parameter.

Also, you can set the IsWhiteSpaceSeparator named parameter to false so that white-space characters within the members themselves are not considered as separators. As an example, see the PathsExample class in the demo application.

Serializing Objects of Type Dictionary

Let's improve our example a little more by adding a dictionary of, say, PossibleItems, and their quantity, as stored in our hypothetical warehouse.

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<PossibleItems, int> ItemQuantitiesDic { get; set; }
}

We instantiate an object as follows:

Dictionary<PossibleItems,int> dicItems =
                 new Dictionary<PossibleItems,int>();
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
};

Here's the XML output:

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

We can still improve the output by renaming the incomprehensible element names, e.g., renaming ItemQuantitiesDic to simply ItemQuantities, and KeyValuePairOfPossibleItemsInt32 (which is the friendly name of KeyValuePair<PossibleItems, Int32>) to, say, ItemInfo. These (simple) changes are possible with the YAXCollection attribute, but we want to introduce the YAXDictionary attribute, which provides more formatting options. So, we add proper attributes to the definition of ItemQuantitiesDic, as follows:

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")]
    [YAXDictionary(EachPairName="ItemInfo")]
    [YAXSerializeAs("ItemQuantities")]
    public Dictionary<PossibleItems, int> ItemQuantitiesDic { get; set; }
}

And, here's the XML output:

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

How about making it more human-readable? For example, let's rename Key to Item and Value to, say, Count. Also, the result seems less complex if we make both the Key and Value parts attributes of the parent ItemInfo element. To accomplish this, make the following changes to the class definition:

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; }

    [YAXDictionary(EachPairName="ItemInfo", KeyName="Item", ValueName="Count",
                   SerializeKeyAs=YAXNodeTypes.Attribute,
                   SerializeValueAs=YAXNodeTypes.Attribute)]
    [YAXSerializeAs("ItemQuantities")]
    public Dictionary<PossibleItems, int> ItemQuantitiesDic { get; set; }
}

Here's the XML output:

<Warehouse Name="Foo Warehousing Ltd.">
  <SiteInfo address="No. 10, Some Ave., Some City, Some Country">
    <SurfaceArea>120000.5</SurfaceArea>
  </SiteInfo>
  <StoreableItems>Item3, Item6, Item9, Item12</StoreableItems>
  <ItemQuantities>
    <ItemInfo Item="Item3" Count="10" />
    <ItemInfo Item="Item6" Count="120" />
    <ItemInfo Item="Item9" Count="600" />
    <ItemInfo Item="Item12" Count="25" />
  </ItemQuantities>
</Warehouse>

It now seems more readable and less complicated. The YAXDictionary attribute needs more explanation. This attribute can receive seven parameters:

  • EachPairName: defines an alias for the element containing the key-value pair.
  • KeyName: defines an alias for the Key element/attribute.
  • ValueName: defines an alias for the Value element/attribute.
  • SerializeKeyAs: specifies whether the key-part should be serialized as an attribute of its containing element, or as its child element.
  • SerializeValueAs: specifies whether the value-part should be serialized as an attribute of its containing element, or as its child element.
  • KeyFormatString: defines a format string, by which the key data will be serialized.
  • ValueFormatString: defines a format string, by which the value data will be serialized.

It is also possible to use both the YAXDictionary and YAXCollection attributes together to produce better results. For example, consider that you want the ItemInfo elements of the previous example be serialized without the containing element ItemQuantities. For this purpose, you can add a YAXCollection attribute, with its serialization type set to RecursiveWithNoContainingElement, as the following code snippet depicts:

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.RecursiveWithNoContainingElement)]
    [YAXDictionary(EachPairName="ItemInfo", KeyName="Item", ValueName="Count",
                   SerializeKeyAs=YAXNodeTypes.Attribute,
                   SerializeValueAs=YAXNodeTypes.Attribute)]
    [YAXSerializeAs("ItemQuantities")]
    public Dictionary<PossibleItems, int> ItemQuantitiesDic { get; set; }
}

Here's the XML output:

<Warehouse Name="Foo Warehousing Ltd.">
  <SiteInfo address="No. 10, Some Ave., Some City, Some Country">
    <SurfaceArea>120000.5</SurfaceArea>
  </SiteInfo>
  <StoreableItems>Item3, Item6, Item9, Item12</StoreableItems>
  <ItemInfo Item="Item3" Count="10" />
  <ItemInfo Item="Item6" Count="120" />
  <ItemInfo Item="Item9" Count="600" />
  <ItemInfo Item="Item12" Count="25" />
</Warehouse>

Serializing Nested Objects

As another improvement, let's see how nested objects are serialized. We define a Person class and add an Owner property of type Person to our hypothetical warehouse.

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; }

    [YAXDictionary(EachPairName = "ItemInfo", KeyName = "Item", ValueName = "Count",
                   SerializeKeyAs = YAXNodeTypes.Attribute,
                   SerializeValueAs = YAXNodeTypes.Attribute)]
    [YAXSerializeAs("ItemQuantities")]
    public Dictionary<PossibleItems, int> ItemQuantitiesDic { get; set; }

    public Person Owner { get; set; }
}

And, create an instance as follows:

Dictionary<PossibleItems,int> dicItems =
                 new Dictionary<PossibleItems,int>();
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 }
};

Here is the XML output:

<Warehouse Name="Foo Warehousing Ltd.">
  <SiteInfo address="No. 10, Some Ave., Some City, Some Country">
    <SurfaceArea>120000.5</SurfaceArea>
  </SiteInfo>
  <StoreableItems>Item3, Item6, Item9, Item12</StoreableItems>
  <ItemQuantities>
    <ItemInfo Item="Item3" Count="10" />
    <ItemInfo Item="Item6" Count="120" />
    <ItemInfo Item="Item9" Count="600" />
    <ItemInfo Item="Item12" Count="25" />
  </ItemQuantities>
  <Owner>
    <SSN>123456789</SSN>
    <Name>John</Name>
    <Family>Doe</Family>
    <Age>50</Age>
  </Owner>
</Warehouse>

Again, we add more formatting attributes to both of the classes. For example, we change the Person class as follows:

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; }
}

The above modification produces the following output:

<Warehouse Name="Foo Warehousing Ltd.">
  <SiteInfo address="No. 10, Some Ave., Some City, Some Country">
    <SurfaceArea>120000.5</SurfaceArea>
  </SiteInfo>
  <StoreableItems>Item3, Item6, Item9, Item12</StoreableItems>
  <ItemQuantities>
    <ItemInfo Item="Item3" Count="10" />
    <ItemInfo Item="Item6" Count="120" />
    <ItemInfo Item="Item9" Count="600" />
    <ItemInfo Item="Item12" Count="25" />
  </ItemQuantities>
  <Owner SSN="123456789">
    <Identification Name="John" Family="Doe" />
    <Age>50</Age>
  </Owner>
</Warehouse>

How about making the owner's SSN appear as an attribute of the Warehouse itself? It is possible in YAXLib to specify serialization addresses, as is popular in file systems. For example, you can use YAXAttributeFor in the following styles:

[YAXAttributeFor("Identification")]
[YAXAttributeFor("./Identification")]
[YAXAttributeFor("../Identification")]
[YAXAttributeFor("../../Identification")]
[YAXAttributeFor("SomeTag/SomeOtherTag/AndSo")]

We want SSN to appear as an attribute for its parent, so we set its serialization location as follows:

public class Person
{
    //[YAXAttributeForClass()]
    [YAXAttributeFor("..")]
    [YAXSerializeAs("OwnerSSN")]
    public string SSN { get; set; }

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

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

    public int Age { get; set; }
}

The XML result is:

<Warehouse Name="Foo Warehousing Ltd." OwnerSSN="123456789">
  <SiteInfo address="No. 10, Some Ave., Some City, Some Country">
    <SurfaceArea>120000.5</SurfaceArea>
  </SiteInfo>
  <StoreableItems>Item3, Item6, Item9, Item12</StoreableItems>
  <ItemQuantities>
    <ItemInfo Item="Item3" Count="10" />
    <ItemInfo Item="Item6" Count="120" />
    <ItemInfo Item="Item9" Count="600" />
    <ItemInfo Item="Item12" Count="25" />
  </ItemQuantities>
  <Owner>
    <Identification Name="John" Family="Doe" />
    <Age>50</Age>
  </Owner>
</Warehouse>

Adding XML Comments

Since one of the main goals of YAXLib is enhancing human readability, it seems useful to have some means of inserting XML comments in the generated XML. You can use the YAXComment attribute to add comments to serialized objects. For example, see the modified version of our Warehouse below:

[YAXComment("General information about a warehouse")]
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; }

    [YAXDictionary(EachPairName = "ItemInfo", KeyName = "Item", ValueName = "Count",
                   SerializeKeyAs = YAXNodeTypes.Attribute,
                   SerializeValueAs = YAXNodeTypes.Attribute)]
    [YAXSerializeAs("ItemQuantities")]
    public Dictionary<PossibleItems, int> ItemQuantitiesDic { get; set; }

    [YAXComment("All the fields are mandatory, please fill all of them")]
    public Person Owner { get; set; }
}

and the generated XML:

<!-- General information about a warehouse -->
<Warehouse Name="Foo Warehousing Ltd.">
  <SiteInfo address="No. 10, Some Ave., Some City, Some Country">
    <SurfaceArea>120000.5</SurfaceArea>
  </SiteInfo>
  <StoreableItems>Item3, Item6, Item9, Item12</StoreableItems>
  <ItemQuantities>
    <ItemInfo Item="Item3" Count="10" />
    <ItemInfo Item="Item6" Count="120" />
    <ItemInfo Item="Item9" Count="600" />
    <ItemInfo Item="Item12" Count="25" />
  </ItemQuantities>
  <!-- All the fields are mandatory, please fill all of them -->
  <Owner SSN="123456789">
    <Identification Name="John" Family="Doe" />
    <Age>50</Age>
  </Owner>
</Warehouse>

Deserialization and Error Handling Schemes

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 Deserialize method in the following style:

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
{
}

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 Deserialize method that accept instances of XmlReader or TextReader as a parameter. Also, there is another method called DeserializeFromFile which accepts the path to the XML input file as a parameter.

No matter which method is used, always use the above style for deserialization; i.e., put the deserialization method within a try block, and always compare the result with null.

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 a 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: YAXLib Exception Classes. To give the developer more control on handling errors, there are some extra features added to YAXLib.

One feature is the ParsingErrors property of the YAXSerializer class. This property is a reference to a collection of YAXExceptions 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 ToString() method to print the list of exceptions in a neat format (this is what is done in the demo application).

Another feature is enabling developers to choose the exception handling policy of the library. This is done by providing the YAXExceptionHandlingPolicies parameter of the constructor of YAXSerializer. This is an enum with three possible values:

  • ThrowWarningsAndErrors: Throws an exception at the time a 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.
  • ThrowErrorsOnly: 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 ParsingErrors property.
  • DoNotThrow: Logs all kinds of exceptions (either labeled as warning or error), and does not throw any of them. The YAXCannotSerializeSelfReferentialTypes and YAXObjectTypeMismatch are two exceptions of this rule, but note that both of these exception classes happen only at serialization time, not during deserialization.

The third parameter of the constructor of the YAXSerializer class specifies the default exception type (Warning or Error) for all exceptions that may happen (the default is Error). We can alter this behavior for a special property of the class we want to serialize, by using the YAXErrorIfMissed 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 missing in the input XML. As an example, consider the ProgrammingLanguage class below:

public class ProgrammingLanguage
{
    public string LanguageName { get; set; }
    public bool IsCaseSensitive { get; set; }
}

We require that the LanguageName property be mandatory, but the IsCaseSensitive property be optional. If the value corresponding to the IsCaseSensitive property is missing in the XML, then we consider it as true. For this purpose, we add attributes to the class definition, as shown below:

public class ProgrammingLanguage
{
    [YAXErrorIfMissed(YAXExceptionTypes.Error)]
    public string LanguageName { get; set; }

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

As seen in the above code snippet, we treat the absence of LanguageName as an error, but we treat the absence of IsCaseSensitive as a warning, and if this happens, we assign the default value of true to this property. Consequently, we use the ThrowErrorsOnly exception handling policy, and treat all other exceptions as warnings. The code below illustrates this while assuming that the input XML is stored in the string variable inputXML.

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());
}

If the input XML is in the following format, then the deserialization finishes successfully:

<ProgrammingLanguage>
  <LanguageName>C#</LanguageName>
  <IsCaseSensitive>true</IsCaseSensitive>
</ProgrammingLanguage>

The following XML input leads to a warning which will be stored in the ParsingErrors collection, but the value of IsCaseSensitive will be set to true.

<ProgrammingLanguage>
  <LanguageName>C#</LanguageName>
  <!-- <IsCaseSensitive>true</IsCaseSensitive> -->
</ProgrammingLanguage>

But, the following XML input leads to an exception being thrown, and therefore, the deserialization process fails.

<ProgrammingLanguage>
  <!-- <LanguageName>C#</LanguageName> -->
  <IsCaseSensitive>true</IsCaseSensitive>
</ProgrammingLanguage>

Choosing the Fields to be Serialized

By default, YAXLib serializes only public properties of the type that is specified. This behavior can be changed for any user-defined class through the YAXSerializableType attribute. This attribute is applicable to classes and structures only, and sets some serialization options related to that special type. Note that existence of an attribute named YAXSerializableType does not mean that the types that do not make use of this attribute are not serializable. In YAXLib, all types are merely serializable. This attribute helps us only in setting and overriding serialization options.

The YAXSerializableType attribute has two named properties:

  • FieldsToSerialize: Through this property, you can choose the type of members to be serialized. The default behavior is that public properties are chosen to be serialized. There are three possible options. PublicPropertiesOnly makes the serializer serialize only the public properties (the default). AllFields serializes public and non-public properties and member variables. In order not to serialize some members, attribute them with the YAXDontSerialize attribute. The third option is AttributedFieldsOnly, which serializes only the members (public or non-public properties or member variables) attributed as YAXSerializableField.
  • Options: Through this property, you can choose whether or not to serialize null references. The default behavior, if not set otherwise by the serializer itself, is to serialize null references. Through this property, this behavior can be overloaded. There are two possible options that can be set: SerializeNullObjects, which makes the serializer serialize all references including null references (the default), and DontSerializeNullObjects, which makes the serializer not serialize null references. Setting the latter option will cause missing properties to be retrieved as null. For a discussion on the benefits of not serializing null objects, see the section Preserving Null-References Identity.

Defining Aliases for Enum Members

YAXLib enables you to define aliases for enum values through the YAXEnum attribute. For example, consider that we have an enum of seasons defined as:

public enum Seasons
{
    First, Second, Third, Fourth
}

Without applying any attributes to our enum, its members will be serialized as First, Second, and so on. But, if we change our enum in the following way:

public enum Seasons
{
    [YAXEnum("Spring")]
    First,

    [YAXEnum("Summer")]
    Second,

    [YAXEnum(" Autumn or fall ")]
    Third,

    [YAXEnum("Winter")]
    Fourth
}

and define and instantiate the SeasonsSample class:

public class SeasonsSample
{
    public Seasons[] ColdSeasons { get; set; }
}

...

SeasonsSample sample = new SeasonsSample()
{
    ColdSeasons = new Seasons[] { Seasons.Third, Seasons.Fourth }
};

The result of serialization of the sample object will be:

<SeasonsSample>
  <ColdSeasons>
    <Seasons>Autumn or fall</Seasons>
    <Seasons>Winter</Seasons>
  </ColdSeasons>
</SeasonsSample>

Since whitespace characters are valid in enum aliases, special care should be taken when a collection of enums is serialized serially. Since the space character is used by default as a separator in such collections, it is recommended that other characters be used as the separator instead of space. Also, enum members which are logically ored together are separated from each other by a comma. Therefore, selecting comma as a separator is not a wise decision either. The following code shows how to solve this problem, by selecting semicolon as the separator:

public class SeasonsSample
{
    [YAXCollection(YAXCollectionSerializationTypes.Serially, 
        IsWhiteSpaceSeparator=false, SeparateBy=";")]
    public Seasons[] ColdSeasons { get; set; }
}

The XML result which is unambiguously deserializable will be:

<SeasonsSample>
  <ColdSeasons>Autumn or fall;Winter</ColdSeasons>
</SeasonsSample>

Serializing Multi-dimensional Arrays

YAXLib is capable of serializing and deserializing multi-dimensional and jagged arrays. For this reason, the serializer generates some meta-data about the dimensions of the array to guide the deserialization process. For example, consider the following class:

public class MultiDimArrays
{
    public int[,] TheSquareMatrix { get; set; }

    public int[][] TheTriangularMatrix { get; set; }

    public static MultiDimArrays GetSampleInstance()
    {
        int[,] matrix = new int[3, 3];
        for (int i = 0; i < matrix.GetLength(0); i++)
            for (int j = 0; j < matrix.GetLength(1); j++)
                matrix[i, j] = i + j + 1;

        MultiDimArrays obj = new MultiDimArrays()
        {
            TheSquareMatrix = matrix,
            TheTriangularMatrix = new int[3][] 
              {new int[] {1}, new int[] {1,2}, new int[] {1,2,3}}
        };

        return obj;
    }
}

Serializing the object gained from the GetSampleInstance static method yields the following XML result:

<MultiDimArrays xmlns:yaxlib="http://www.sinairv.com/yaxlib/">
  <TheSquareMatrix yaxlib:dims="3,3">
    <Int32>1</Int32>
    <Int32>2</Int32>
    <Int32>3</Int32>
    <Int32>2</Int32>
    <Int32>3</Int32>
    <Int32>4</Int32>
    <Int32>3</Int32>
    <Int32>4</Int32>
    <Int32>5</Int32>
  </TheSquareMatrix>
  <TheTriangularMatrix>
    <Array1OfInt32>
      <Int32>1</Int32>
    </Array1OfInt32>
    <Array1OfInt32>
      <Int32>1</Int32>
      <Int32>2</Int32>
    </Array1OfInt32>
    <Array1OfInt32>
      <Int32>1</Int32>
      <Int32>2</Int32>
      <Int32>3</Int32>
    </Array1OfInt32>
  </TheTriangularMatrix>
</MultiDimArrays>

The above XML is successfully deserialized into an object with arrays with same dimensions. The only special thing to notice about the above XML result is the yaxlib:dims attribute of the TheSquareMatrix element. This attribute provides the needed meta-data to deserialize the matrix successfully.

Serializing Objects Through a Reference to Their Base-Class or Interface

Consider that you hold a collection of objects through a reference to their common interface or base-class and you need to serialize them in a way that their real type could be successfully determined during deserialization. This is what happens when you use a non-generic collection class; e.g., ArrayList is merely a list of objects. Successful serialization and deserialization of these kind of objects are possible in YAXLib, since it serializes meta-data to aid loading the real type of object at deserialization time. For example, consider the following class:

public class BaseClassRefSample
{
    public IEnumerable RefToIEnumerable1 { get; set; }
    public IEnumerable RefToIEnumerable2 { get; set; }

    public List<object> ListOfObjects { get; set; }

    public static BaseClassRefSample GetSampleInstance()
    {
        int[] someIntArray = new int[] { 1, 2, 3 };
        string[] someStrArray = new string[] { "Hi", "Hello" };

        List<object> lst = new List<object>();
        lst.Add(7); // adding an integer
        lst.Add(3.14); // adding a double
        lst.Add("Congrats"); // adding a string
        lst.Add(StringSplitOptions.RemoveEmptyEntries); // adding some enum member

        return new BaseClassRefSample()
        {
            RefToIEnumerable1 = someIntArray,
            RefToIEnumerable2 = someStrArray,
            ListOfObjects = lst
        };
    }
}

The above class has three members. Two members are references to an IEnumerable instance. In the GetSampleInstance method, the first reference is referred to an array of integers, and the second one is assigned to an array of strings. So, two properties of similar appearance are referring to two different types of objects. The third property is a list of objects which is filled with an integer, a double number, a string, and an enum member. The XML result is the following:

<FreeSample xmlns:yaxlib="http://www.sinairv.com/yaxlib/">
  <RefToIEnumerable1 yaxlib:realtype="System.Int32[]">
    <Int32>1</Int32>
    <Int32>2</Int32>
    <Int32>3</Int32>
  </RefToIEnumerable1>
  <RefToIEnumerable2 yaxlib:realtype="System.String[]">
    <String>Hi</String>
    <String>Hello</String>
  </RefToIEnumerable2>
  <ListOfObjects>
    <Object yaxlib:realtype="System.Int32">7</Object>
    <Object yaxlib:realtype="System.Double">3.14</Object>
    <Object yaxlib:realtype="System.String">Congrats</Object>
    <Object yaxlib:realtype="System.StringSplitOptions">RemoveEmptyEntries</Object>
  </ListOfObjects>
</FreeSample>

Note the use of the yaxlib:realtype attributes that generate meta-data about the real type of the specified references. Through these meta-data, the deserializer will be able to load the true underlying types and assign values to them.

Preserving Null-References Identity

One of the primary objectives of a serialization library should be that the object before serialization must be member-wise equal to the object deserialized. This is violated sometimes if some of the object's properties are null. For example, consider the following class containing only a List<>:

public class CollectionOfItems
{
    public List<int> TheCollection { get; set; }
}

And also, consider the following two unequal objects, namely obj1 and obj2:

CollectionOfItems obj1 = new CollectionOfItems()
{
    TheCollection = new List<int>()
};

CollectionOfItems obj2 = new CollectionOfItems()
{
    TheCollection = null
};

As seen in the above code snippet, obj1 contains a non-null but empty List<>, whereas the corresponding property in obj2 is null. Now, the problem is that, they both produce the same XML output:

<CollectionOfItems>
  <TheCollection />
</CollectionOfItems>

One solution is to prevent the serialization of null properties. Of course, this solution is not recommended if the structure of the XML output is important or it is going to be validated with some schema.

To accomplish this, the developer might specify the serialization options through the YAXSerializationOptions enumeration. Currently, this enumeration has only two members:

  • DontSerializeNullObjects which prevents serializing null properties
  • SerializeNullObjects which serializes all properties (the default)

If the developer sets the serialization option to DontSerializeNullObjects through the constructor of YAXSerializer, then we will see that the generated XML of obj1 and obj2 of the example above are different. More importantly, the objects before serialization and after deserialization are equal. This is the XML generated for obj2 with the option DontSerializeNullObjects set:

<CollectionOfItems />

Formatting Data Items

YAXLib provides the YAXFormat attribute with which developers can specify format strings for properties to be serialized. For example, if you want to serialize a double variable with only three digits of precision after floating point, then you can use the F03 format string, through the YAXFormat attribute.

This attribute also applies to collections of items, but it does not work for objects of type Dictionary. Instead, you can use the YAXDictionary attribute and specify the KeyFormatString and ValueFormatString parameters.

If the format string is not supported in the .NET Framework, then an exception of type YAXInvalidFormatProvided is thrown/logged.

Special care should be taken while using format strings. Some data types can be serialized successfully with some format strings, but they might not be deserialized later. For example, you might accidentally use the F03G format string for type double.

As an example of the formatting capabilities of YAXLib, see the following class definition:

public class FormattingExample
{
    public DateTime CreationDate { get; set; }
    public DateTime ModificationDate { get; set; }
    public double PI { get; set; }
    public List<double> NaturalExp { get; set; }
    public Dictionary<double, double> SomeLogarithmExample { get; set; }
}

Here's the code to create an instance of this class:

List<double> lstNE = new List<double>();
for (int i = 1; i <= 4; ++i)
    lstNE.Add(Math.Exp(i));

Dictionary<double, double> dicLog = new Dictionary<double, double>();
for (double d = 1.5; d <= 10; d *= 2)
    dicLog.Add(d, Math.Log(d));

return new FormattingExample()
{
    CreationDate = new DateTime(2007, 3, 14),
    ModificationDate = DateTime.Now,
    PI = Math.PI,
    NaturalExp = lstNE,
    SomeLogarithmExample = dicLog
};

And, this is the XML output for our instance:

<FormattingExample>
  <CreationDate>2007-03-14T00:00:00</CreationDate>
  <ModificationDate>2009-04-13T17:40:11.56+04:30</ModificationDate>
  <PI>3.1415926535897931</PI>
  <NaturalExp>
    <Double>2.7182818284590451</Double>
    <Double>7.38905609893065</Double>
    <Double>20.085536923187668</Double>
    <Double>54.598150033144236</Double>
  </NaturalExp>
  <SomeLogarithmExample>
    <KeyValuePairOfDoubleDouble>
      <Key>1.5</Key>
      <Value>0.40546510810816438</Value>
    </KeyValuePairOfDoubleDouble>
    <KeyValuePairOfDoubleDouble>
      <Key>3</Key>
      <Value>1.0986122886681098</Value>
    </KeyValuePairOfDoubleDouble>
    <KeyValuePairOfDoubleDouble>
      <Key>6</Key>
      <Value>1.791759469228055</Value>
    </KeyValuePairOfDoubleDouble>
  </SomeLogarithmExample>
</FormattingExample>

Now, let's add some formatting to the data items of our class definition:

public class FormattingExample
{
    [YAXFormat("D")]
    public DateTime CreationDate { get; set; }

    [YAXFormat("d")]
    public DateTime ModificationDate { get; set; }

    [YAXFormat("F05")]
    public double PI { get; set; }

    [YAXFormat("F03")]
    public List<double> NaturalExp { get; set; }

    [YAXDictionary(KeyFormatString="F02", ValueFormatString="F05")]
    public Dictionary<double, double> SomeLogarithmExample { get; set; }
}

The result of the XML serialization would be:

<FormattingExample>
  <CreationDate>Wednesday, March 14, 2007</CreationDate>
  <ModificationDate>4/13/2009</ModificationDate>
  <PI>3.14159</PI>
  <NaturalExp>
    <Double>2.718</Double>
    <Double>7.389</Double>
    <Double>20.086</Double>
    <Double>54.598</Double>
  </NaturalExp>
  <SomeLogarithmExample>
    <KeyValuePairOfDoubleDouble>
      <Key>1.50</Key>
      <Value>0.40547</Value>
    </KeyValuePairOfDoubleDouble>
    <KeyValuePairOfDoubleDouble>
      <Key>3.00</Key>
      <Value>1.09861</Value>
    </KeyValuePairOfDoubleDouble>
    <KeyValuePairOfDoubleDouble>
      <Key>6.00</Key>
      <Value>1.79176</Value>
    </KeyValuePairOfDoubleDouble>
  </SomeLogarithmExample>
</FormattingExample>

Multi-Stage Deserialization

Some applications require multi-stage deserialization, in that later deserialized values override previous ones. An example of this technique can be observed in ASP.NET applications in which the values in the web.conf file in each directory override those settings previously defined in upper level directories. This technique can be accomplished using YAXLib through the SetDeserializationBaseObject method. The steps are as follows:

  1. Deserialize the top-level XML file into an object, say obj.
  2. Through the call yaxSerializer.SetDeserializationBaseObject(obj), set the resulting object as the deserializer's base object for the next stage of deserialization.
  3. Deserialize the next stage XML file into the same object, obj.
  4. If there are any stages (XML files) remaining, go back to step 2.

The Problem with Classes without a Default Constructor

YAXLib needs that the classes used with it do have a default constructor. But, there are many classes already defined in the .NET Framework that do not have a default constructor (e.g., the System.Drawing.Color struct). To overcome this problem, we can create properties of some other types (e.g., string) in which the get and set methods convert between the two types (e.g., convert string to Color and vice versa). Here's an example that uses a property of type string instead of a property of type Color:

public class ColorExample
{
    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);
            }
        }
    }
}

Here is the XML generated by serializing an object of the above class:

<SelfReferentialExample>
  <TheColor>#FF0000FF</TheColor>
</SelfReferentialExample>

The Problem with Self-Referential Classes

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 YAXCannotSerializeSelfReferentialTypes exception is thrown if such classes are encountered during the serialization process. This is one of the few YAXLib exception classes that cannot be turned off even if the exception-handling policy is set to DoNotThrow. So, it is recommended that you always put your serialization code within try blocks. To overcome this problem, the same technique for solving The Problem with Classes without a Default Constructor can be used.

YAXLib Attributes

In this section, we list and describe all attribute classes provided in the YAXLib library. Note that in the descriptions that follow, by the term field, we mean a member variable which could be either public or non-public.

  • YAXSerializeAs: Defines an alias for the field, property, class, or struct under which it will be serialized. This attribute is applicable to fields, properties, classes, and structs.
  • YAXDontSerialize: Prevents serialization of some field or property. This attribute is applicable to fields and properties.
  • YAXAttributeForClass: Makes a property to appear as an attribute for the enclosing class (i.e., the parent element), if possible. This attribute is applicable to fields and properties.
  • YAXAttributeFor: Makes a field or property to appear as an attribute for another element, if possible. This attribute is applicable to fields and properties.
  • YAXElementFor: Makes a property or field to appear as a child element for another element. This attribute is applicable to fields and properties.
  • YAXCollection: Controls the serialization of collection instances. This attribute is applicable to fields and properties. For more details and examples, see the section: Serializing Objects of Collection Classes.
  • YAXDictionary: Controls the serialization of objects of type Dictionary. This attribute is applicable to fields and properties.
  • YAXFormat: Specifies the format string provided for serializing data. The format string is the parameter passed to the ToString method. If this attribute is applied to collection classes, the format, therefore, is applied to the collection members. This attribute is applicable to fields and properties.
  • YAXEnum: Specifies an alias for an enum member. This attribute is applicable to enum members.
  • YAXSerializableType: Sets or overrides the default serialization behaviors. This attribute is optional, and is applicable to classes and structures.
  • YAXSerializableField: Add this attribute to properties or fields which you wish to be serialized, when the enclosing class uses the YAXSerializableType attribute in which FieldsToSerialize has been set to AttributedFieldsOnly. This attribute is applicable to fields and properties.
  • YAXErrorIfMissed: Specifies the behavior of the deserialization method if the element/attribute corresponding to this property is missing in the XML input. This attribute is applicable to fields and properties.
  • YAXNotCollection: Specifies that a particular class, or a particular property or variable type, that is driven from IEnumerable should not be treated as a collection class/object. This attribute is applicable to fields, properties, classes, and structs.
  • YAXComment: Creates a comment node per each line of the comment string provided. This attribute is applicable to classes, structures, fields, and properties.

YAXLib Exception Classes

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 the YAXException class.

  • Exceptions raised during serialization:
    • YAXBadLocationException: Raised when the location of the serialization specified cannot be created or cannot be read from.
    • YAXAttributeAlreadyExistsException: Raised when trying to serialize an attribute where another attribute with the same name already exists.
    • YAXCannotSerializeSelfReferentialTypes: Raised when trying to serialize self-referential types. This exception cannot be turned off.
    • YAXObjectTypeMismatch: Raised when the object provided for serialization is not of the type provided for the serializer. This exception cannot be turned off.
    • YAXInvalidFormatProvided: Raised when an object cannot be formatted with the format string provided.
  • Exceptions raised during deserialization:
    • YAXAttributeMissingException: Raised when the attribute corresponding to some property is not present in the given XML file.
    • YAXElementMissingException: Raised when the element corresponding to some property is not present in the given XML file.
    • YAXBadlyFormedInput: Raised when the value provided for some property in the XML input cannot be converted to the type of the property.
    • YAXPropertyCannotBeAssignedTo: Raised when the value provided for some property in the XML input cannot be assigned to the property.
    • YAXCannotAddObjectToCollection: Raised when some member of the collection in the input XML cannot be added to a collection object.
    • YAXDefaultValueCannotBeAssigned: Raised when the default value specified by YAXErrorIfMissedAttribute could not be assigned to the property.
    • YAXBadlyFormedXML: Raised when the XML input does not follow standard XML formatting rules.

When Not to Use YAXLib

The emphasis of YAXLib on the structure and 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 a 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 not going to be edited by a human user later, and your objects do not use collection classes such as Dictionary<,>, then it might be better to use the XmlSerializer class of the .NET Framework, because your code would be more portable.

History

  • May 17, 2010: YAXLib is now hosted on CodePlex.
  • April 16, 2010: YAXLib 2.0, with a lot more features.
  • December 22, 2009: Some bug-fixes, and added a download link to an SVN repository so that every bug-fix does not require an update to the CodeProject article.
  • April 13, 2009: YAXLib 1.1.
  • March 13, 2009: First version.

For more detailed information, see the change-log file.

License

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

Share

About the Author

Sina Iravanian
Software Developer
Australia Australia
A software designer and developer
Follow on   Twitter   Google+

Comments and Discussions

 
GeneralRe: Problem serializing DataTable PinmemberSina Iravanian23-Oct-12 23:11 
QuestionDeserializaiton of Xml Comments and readonly properties Pinmemberrebulanyum17-Oct-12 1:47 
AnswerRe: Deserializaiton of Xml Comments and readonly properties PinmemberSina Iravanian17-Oct-12 4:18 
QuestionRe: Deserializaiton of Xml Comments and readonly properties Pinmemberrebulanyum17-Oct-12 23:16 
AnswerRe: Deserializaiton of Xml Comments and readonly properties PinmemberSina Iravanian18-Oct-12 1:44 
Questionhow to serialize to a one line string (no formatting) Pinmemberseer_tenedos217-Jan-12 9:58 
AnswerRe: how to serialize to a one line string (no formatting) PinmemberSina Iravanian17-Jan-12 20:09 
QuestionWhy deserializer need a type? PinmemberPhan Dung18-Sep-11 17:14 
AnswerRe: Why deserializer need a type? PinmemberSina Iravanian18-Sep-11 19:28 
AnswerRe: Why deserializer need a type? PinmemberSina Iravanian15-Oct-11 0:28 
QuestionSimple objects serializing PinmemberFCBCoder21-Jun-11 20:35 
AnswerRe: Simple objects serializing PinmemberSina Iravanian22-Jun-11 0:02 
QuestionNamespace inclusion Pinmemberrobert austin7-Jun-11 4:06 
AnswerRe: Namespace inclusion PinmemberSina Iravanian7-Jun-11 5:08 
GeneralWCF and YAXLib PinmemberMib317-Jun-11 2:10 
GeneralRe: WCF and YAXLib PinmemberSina Iravanian7-Jun-11 5:17 
GeneralSerializing collection of base class Pinmemberseer_tenedos224-May-11 2:23 
GeneralRe: Serializing collection of base class PinmemberSina Iravanian24-May-11 23:34 
GeneralRe: Serializing collection of base class PinmemberTomas Deml25-May-11 0:56 
GeneralRe: Serializing collection of base class PinmemberSina Iravanian25-May-11 1:39 
GeneralRe: Serializing collection of base class Pinmemberseer_tenedos225-May-11 12:09 
QuestionHow to create a CData section during serialization PinmemberNorbert Bietsch22-Apr-11 13:31 
AnswerRe: How to create a CData section during serialization PinmemberSina Iravanian22-Apr-11 21:51 
GeneralRe: How to create a CData section during serialization PinmemberNorbert Bietsch23-Apr-11 4:00 
GeneralRe: How to create a CData section during serialization PinmemberSina Iravanian23-Apr-11 4:48 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140814.1 | Last Updated 2 Oct 2012
Article Copyright 2009 by Sina Iravanian
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid