Blog Post RSS ?

Blogs » .NET » Generic Dictionaries vs. the XmlSerializer
 

Generic Dictionaries vs. the XmlSerializer


  • Save to
    Del.icio.us

by Wyatt Barnett

One of the neatest new features of .NET 2.0 is the concept of Generics. And one of the most common uses of this feature is in the new GenericCollections. Anyone who spent quality time extending CollectionBase and DictionaryBase in 1.1 (or just cheating using CodeSmith’s collection gen) will understand how useful these collections are.

Another of the very handy features of .NET is the built-in, nearly automagic XmlSerialization. Presuming one follows a few simple rules—principally having a blank, default constructor and making sure all properties implement Get and Set accessors—one can instantaneously serialze any object to Xml. This tactic, unfortunately, falls apart when one attempts to make use of the new Generic dictionary as a member of an object one wishes to XmlSerialize. Rather than a bit of xml, you get a nice little exception stating “There was an error reflecting type ‘your type name here’”.

There are a number of ways to workaround this issue. Most involve implementing IXmlSerializable, which tends to lead to writing a lot of repetitive code. And these techniques are a good option if you are writing to and reading from a very specific schema. However, many times, one just needs to get something serialized in a quick and dirty, but working manner for use with, say, some internal Web Service or some kind of temporary storage.

In that case, one can use the following technique:

  1. First, decorate the Dictionary<TKey, TValue> property with an XmlIgnore attribute. This tells the XmlSerializer to ignore that property, eliminating the reflection error.
  2. Create a new public property that takes and returns an array of DictionaryEntry objects. I tend to name these specially (_x_DictionaryName) so that it is clear one should not generally use this property.
  3. Serialize away.

 

In case the above is a bit obtuse, see below for an example:


using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;

namespace SPBlog.DictionarySerialization
{
    [XmlRoot("StuffContainer")]
    public class Serializable
    {
        public Serializable() { }

        private Dictionary<string, int> stuff=new Dictionary<string,int>();
        
        /// <summary>
        /// Public stuff dictionary.
        /// </summary>
        /// <remarks>
        /// Note the XmlIgnore attribute.
        /// </remarks>
        [XmlIgnore()]
        public Dictionary<string, int> Stuff
        {
            set { stuff = value; }
            get { return stuff; }
        }

        
        /// <summary>
        /// Property created expressly for the XmlSerializer
        /// </summary>
        /// <remarks>
        /// Note the XML Serialiazation attributes; they control what elements are named when this object is serialized.
        /// </remarks>
        [XmlArray("Stuff")]
        [XmlArrayItem("StuffLine", Type=typeof(DictionaryEntry))]
        public DictionaryEntry[] _x_Stuff
        {
            get
            {
                //Make an array of DictionaryEntries to return
                DictionaryEntry[] ret=new DictionaryEntry[Stuff.Count];
                int i=0;
                DictionaryEntry de;
                //Iterate through Stuff to load items into the array.
                foreach (KeyValuePair<string, int> stuffLine in Stuff)
                {
                    de = new DictionaryEntry();
                    de.Key = stuffLine.Key;
                    de.Value = stuffLine.Value;
                    ret[i]=de;
                    i++;
                }
                return ret;
            }
            set
            {
                Stuff.Clear();
                for (int i=0; i<value.Length; i++)
                {
                    Stuff.Add((string)value[i].Key, (int)value[i].Value);
                }
            }
        }
    }
}

Which creates the following Xml:


<?xml version="1.0" encoding="utf-8"?>
<StuffContainer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Stuff>
    <StuffLine>
      <Key xsi:type="xsd:string">One</Key>
      <Value xsi:type="xsd:int">1</Value>
    </StuffLine>
    <StuffLine>
      <Key xsi:type="xsd:string">Two</Key>
      <Value xsi:type="xsd:int">2</Value>
    </StuffLine>
    <StuffLine>
      <Key xsi:type="xsd:string">Three</Key>
      <Value xsi:type="xsd:int">3</Value>
    </StuffLine>
    <StuffLine>
      <Key xsi:type="xsd:string">Four</Key>
      <Value xsi:type="xsd:int">4</Value>
    </StuffLine>
  </Stuff>
</StuffContainer>

Now, as you can see above, the DictionaryEntry deserializes in an interesting way—it adds a type attribute to the Key and Value nodes. One could easily modify this technique to use a custom object that serializes to specific types if one needs a bit more finite control of the output.

This post has 3 responses so far

  1. Isn’t the problem with serializing Dictionaries / Hastables realated to circular refereces? Objects with circular references can’t be xml serialized by the “automagic XmlSerialization”.

    cheers,
    Rui

     
  2. Not exactly certain what you are hitting at here Rui. The issue appears to me to be that one cannot serialize KeyValuePair, for reasons I admittedly am not certain of. In addition, objects accessible with indexers, eg lists, are serializable without any special voodoo.

     
  3. Here is a XML Serializable Generic Dictionary.

    ~ Paul

     

Sponsored Links

Leave a response

You are not logged in, log in with your SitePoint Forum username and password.

-OR- Post Anonymously

* Make sure any code samples are escaped (i.e. ‘<b>’ becomes ‘&lt;b&gt;’).

If not logged in, your comments will be placed in a moderation queue. This means your comment may not appear until one of our moderators approves it.

SitePoint Marketplace

Buy and sell Websites, templates, domain names, hosting, graphics and more.

Logo Design, Web page Design and more!

99designs

  • Custom logo designs created ‘just for you’.
  • Pick the design you like best.
  • Only pay if you’re satisfied with the result.