XML Serializable Generic Dictionary

Posted Wednesday, May 03, 2006 12:13 PM by pwelter34

XML Serializable Generic Dictionary

For some reason, the generic Dictionary in .net 2.0 is not XML serializable.  The following code snippet is a xml serializable generic dictionary.  The dictionary is serialzable by implementing the IXmlSerializable interface. 

    using System;

    using System.Collections.Generic;

    using System.Text;

    using System.Xml.Serialization;

 

    [XmlRoot("dictionary")]

    public class SerializableDictionary<TKey, TValue>

        : Dictionary<TKey, TValue>, IXmlSerializable

    {

        #region IXmlSerializable Members

        public System.Xml.Schema.XmlSchema GetSchema()

        {

            return null;

        }

 

        public void ReadXml(System.Xml.XmlReader reader)

        {

            XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));

            XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));

 

            bool wasEmpty = reader.IsEmptyElement;

            reader.Read();

 

            if (wasEmpty)

                return;

 

            while (reader.NodeType != System.Xml.XmlNodeType.EndElement)

            {

                reader.ReadStartElement("item");

 

                reader.ReadStartElement("key");

                TKey key = (TKey)keySerializer.Deserialize(reader);

                reader.ReadEndElement();

 

                reader.ReadStartElement("value");

                TValue value = (TValue)valueSerializer.Deserialize(reader);

                reader.ReadEndElement();

 

                this.Add(key, value);

 

                reader.ReadEndElement();

                reader.MoveToContent();

            }

            reader.ReadEndElement();

        }

 

        public void WriteXml(System.Xml.XmlWriter writer)

        {

            XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));

            XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));

 

            foreach (TKey key in this.Keys)

            {

                writer.WriteStartElement("item");

 

                writer.WriteStartElement("key");

                keySerializer.Serialize(writer, key);

                writer.WriteEndElement();

 

                writer.WriteStartElement("value");

                TValue value = this[key];

                valueSerializer.Serialize(writer, value);

                writer.WriteEndElement();

 

                writer.WriteEndElement();

            }

        }

        #endregion

    }

Update: Fixed bug Justin pointed out by adding an extra reader.ReadEndElement() and by checking the IsEmptyElement property.

Filed under:

Comments

# re: XML Serializable Generic Dictionary

Wednesday, May 03, 2006 2:38 PM by Justin-Josef Angel

I wrote about this issue five months back at and added a CodeSmith generation template:
http://www.justinangel.net/e/CommentView,guid,55167d3d-b4ac-40be-9909-402e3783edab.aspx

BTW, your code is buggy. If you put this Generic dictionary as part of another class it will not desirilize properly. The read method is missing a few reads so the XmlReader will not be set correctly for any items in the XML after this Generic dictionary.

# re: XML Serializable Generic Dictionary

Wednesday, May 03, 2006 2:44 PM by Paul Welter

Justin,

Do you have an example of what will cause it to fail? I've done a lot of testing but must have missed a use case. Thanks for the heads up.

~ Paul

# re: XML Serializable Generic Dictionary

Wednesday, May 03, 2006 4:12 PM by Justin-Josef Angel

Try and create a class with the SerializableAttribute and properties of type string, SerializableDictionary, string (in that order).

Make a webservice method that returns this class (with some content, meaning that string should have a value and the dirctionary should have at least two values).

Look the the XML the webservice generates and see it's OK.

Create a proxy to the webservice and call the webservice method you just created. The class you deserialize will have no value for the string that comes after the SerializableDictionary. (At least i think it won't). If i'm right and that issue does exist it's because you don't read the </dictionary> element at the end of ReadXml.

# re: XML Serializable Generic Dictionary

Wednesday, May 03, 2006 4:20 PM by Paul Welter

Ok, I fixed the issue. I got about 30 test cases for this now. Should be fairly solid. No guarantees of course. :)

~ Paul

# re: XML Serializable Generic Dictionary

Monday, May 15, 2006 4:04 AM by Ward Bekker

Hi Paul,

Thanks for creating this, saved me time!

Ward

# re: XML Serializable Generic Dictionary

Monday, May 15, 2006 4:08 PM by Brian

This doesn't seem to work if you have a dictionary of custom types.

I.E. SerializableDictionary<int, MyObject>

# re: XML Serializable Generic Dictionary

Monday, May 15, 2006 4:11 PM by Paul Welter

Brian,

It should work as long as MyObject is Xml Serializable. Try to serialize MyObject first to see if it works.

~ Paul

# re: XML Serializable Generic Dictionary

Monday, May 15, 2006 4:13 PM by Brian

Nevermind! Ignore previous comment.

# re: XML Serializable Generic Dictionary

Monday, May 15, 2006 4:14 PM by Brian

Paul it does, my goof. Thanks this is very helpful.

# re: XML Serializable Generic Dictionary

Monday, August 28, 2006 10:45 AM by Mark

Thanks, Paul. This is really neat. I have a WebMethod returning a SerializableDictionary. This works fine when I view the service in a browser, & call the method there, but somehow does not work from a fat-client. In fact the generated wsdl does not show the response-type as above. So, am not sure whether i need to do anything special to get it in my proxy?

# re: XML Serializable Generic Dictionary

Monday, September 25, 2006 4:36 AM by Matthias

Thanks for your code-example, but I get an error-message, by using this solution. I define the SerializableDictionary, the key (an enumeration) and value (a struct with two items from datatype string) in an own ClassLibrary. When I use a method in a WebService1 like this public SerializableDictionary testSD1() { SerializableDictionary sd = new SerializableDictionary(); sd.Add(eKeys.key1, new sValues("value1", "value2")); return sd; } It works. When I test it, i get an XML-Document with all Information. When I use a second method in a WebService2: [WebMethod] public System.Xml.XmlDocument testSD4() { localhost.Service ws = new localhost.Service(); datatype.SerializableDictionary sd; System.Data.DataSet ds = ws.testSD1(); System.Xml.XmlDocument xd = new System.Xml.XmlDocument(); xd.LoadXml(ds.GetXml()); return xd; } I recieve just this: Can you help me please? Thanks.

# re: XML Serializable Generic Dictionary

Monday, October 16, 2006 7:28 AM by Pablo

Great piece of code. Thanks very much.

# re: XML Serializable Generic Dictionary

Thursday, October 26, 2006 8:48 PM by Srivalli

Hi, Thanks for the code... I appreciate your effort. I'm having some problems though.... 1. When I used the code as is, I got this error ... [SerializationException: Type 'Microsoft.Exchange.HostedServices.Clients.Utilities.SerializableDictionary`2[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[Microsoft.Exchange.HostedServices.Clients.Membership.User, Microsoft.Exchange.HostedServices.Clients.Membership, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]' in Assembly 'Microsoft.Exchange.HostedServices.Clients.Utilities, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.] I marked it as [Serializable] and then I got the following error... {"The constructor to deserialize an object of type 'Microsoft.Exchange.HostedServices.Clients.Utilities.SerializableDictionary`2[System.String,Microsoft.Exchange.HostedServices.Clients.Membership.User]' was not found."} Then I added the following... protected SerializableDictionary(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } Now there are no errors, but the serialization/deserialization didn't seem to work at all. ReadXml and WriteXml methods were never called... BTW, I'm storing SerializableDictionary object in session as follows... HttpContext.Current.Session["UserAccounts"] = new SerializableDictionary(); ((SerializableDictionary)HttpContext.Current.Session["UserAccounts"])[emailAddress] = user; I'm stuck... Any help/feedback is appreciated. Thank you, Srivalli

# re: XML Serializable Generic Dictionary

Friday, October 27, 2006 6:31 AM by Tatworth

A sample peice of C# code to exercise this in unit tests would appreciated.

# Web 2.0 &raquo; Blog Archives &raquo; Merriam-Webster&#8217;s Spanish-English Dictionary 2.0 from email marketing

# Web 2.0 &raquo; Blog Archives &raquo; Smartphone.net - Windows Mobile Smartphone Software, News, Etc

# Serializable Generic Dictionary

Thursday, June 07, 2007 2:09 AM by Telligent.Interns[0] = "Ryan Hoffman";

In CommunityServer Enterprise Reporting ,we&#39;re using a neat web service to make our architecture

# Web 2.0 &raquo; Blog Archives &raquo; Find Shorter Oxford English Dictionary Windows Version 2.0

Pingback from  Web 2.0  &raquo; Blog Archives   &raquo; Find Shorter Oxford English Dictionary Windows Version 2.0

# re: XML Serializable Generic Dictionary

Sunday, July 15, 2007 6:55 PM by Balslev

Just want to let you know that your little piece works wonders, and saved me quite a few headaches.

Thanks for sharing!

# JSON in C# (Part 1)

Wednesday, August 01, 2007 12:53 AM by Nimble Coder

JSON in C# (Part 1)

# Code Pagoda &raquo; Blog Archive &raquo; Pass Dictionary Object Params To Your Web Service

Pingback from  Code Pagoda  &raquo; Blog Archive   &raquo; Pass Dictionary Object Params To Your Web Service

# re: XML Serializable Generic Dictionary

Wednesday, September 12, 2007 10:03 AM by Gary

Great snippet for sure! Thanks for sharing it with us! I am having a problem, however, I hoped I could get some help with:

I have a web service with a webmethod (where I expect a SerializableDictionary<string,string> as a parameter). When I generate the client-proxy the reference.cs auto-generated class shows that parameter to be System.Data.Dataset instead of MyNamespace.SerializableDictionary<string,string>. Any attempts thus far to generate the schema manually has lead to dead ends ...

How can I (other than manually) Specify the schema for serializing the SerializableDisctionary so that the client "sees" a serializable dictionary as the parameter to this web method?

# re: XML Serializable Generic Dictionary

Wednesday, September 19, 2007 8:58 PM by Thomas

@Srivalli

You have to call the base constructor of the Dictionary class it's inheriting to get it to work. Adding the following constructors should get it to work exactly like the real Dictionary implementation:

       public SerializableDictionary()

           : base()

       {

       }

       public SerializableDictionary(IDictionary<TKey, TValue> dictionary)

           : base(dictionary)

       {

       }

       public SerializableDictionary(IEqualityComparer<TKey> comparer)

           : base(comparer)

       {

       }

       public SerializableDictionary(int capacity)

           : base(capacity)

       {

       }

       public SerializableDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer)

           : base(dictionary, comparer)

       {

       }

       public SerializableDictionary(int capacity, IEqualityComparer<TKey> comparer)

           : base(capacity, comparer)

       {

       }

       protected SerializableDictionary(SerializationInfo info, StreamingContext context)

           : base(info, context)

       {

       }

# re: XML Serializable Generic Dictionary

Friday, September 21, 2007 5:25 PM by Chad

Thanks for putting this out there.  Worked like a charm!

# re: XML Serializable Generic Dictionary

Sunday, October 14, 2007 11:07 AM by Code

Pingback from code-news.blogspot.com

# re: XML Serializable Generic Dictionary

Friday, October 26, 2007 6:53 PM by Brian Miedlar

@gary

You must bind an XmlSchemaProvider in order to get the type to change on a remote web service.

I built upon Paul's version for working VB code using a dictionary of type (string, object)

miedlar.com/.../serializabledictionary

<a href='miedlar.com/.../serializabledictionary'>here</a>

# huseyint.com &raquo; XML Serializable Generic Dictionary tipi

Sunday, December 02, 2007 9:50 AM by huseyint.com » XML Serializable Generic Dictionary tipi

Pingback from  huseyint.com &raquo; XML Serializable Generic Dictionary tipi

# re: XML Serializable Generic Dictionary

Thursday, December 13, 2007 5:50 AM by alastair green

Hello,

I get an exception when I try to serialize a property that uses this.  The problem seems to be that I have decorated the property with XmlArrayAttribute and XmlArrayItemAttribute.  Reading the msdn docs, this should be valid for any type that implements IEnumerable, as your class inherits from Dictionary I should be ok.  Any suggestions?

SerializableDictionary<string,object> _objectTypes = new SerializableDictionary<string,object>();

       [XmlArray("object_definitions")]

       [XmlArrayItem(typeof(UiConfiguratorPlugin), ElementName = "object_type", Namespace = XMLNamespaces.UiConfiguratorPlugin)]

       [XmlArrayItem(typeof(ProtocolRunnerPlugIn), ElementName = "object_type", Namespace = XMLNamespaces.ProtocolRunnerPlugIn)]

       //[XmlAnyElement("object_type")]      

       public SerializableDictionary<string, object> ObjectTypes

       {

           get { return _objectTypes; }

           set { _objectTypes = value; }

       }

# re: XML Serializable Generic Dictionary

Thursday, December 13, 2007 10:38 AM by Adam

Sorry if its been posted already, but if you are trying to put this collection into ViewState, you will need to explicitly implement a constructor on the Dictionary for it to deserialise properly.   I implemented them all explicitly as pass-through methods, simply calling base.

Please refer to:

michhes.blogspot.com/.../generic-dictionary-wrapper-class-cannot.html

Thanks.

# re: XML Serializable Generic Dictionary

Monday, December 31, 2007 11:40 AM by Yury

At string writer.WriteStartElement("item");

InvalidOperationException :

Token StartElement in state EndRootElement would result in an invalid XML document. Make sure that the ConformanceLevel setting is set to ConformanceLevel.Fragment or ConformanceLevel.Auto if you want to write an XML fragment.

# re: XML Serializable Generic Dictionary

Wednesday, January 09, 2008 4:56 PM by Tim

In my thin client i'm unable to properly parse the xml generated by this class. It treats the SerializedDictionary object as a DataSet, but the data set is empty.

Help!

# re: XML Serializable Generic Dictionary

Friday, January 18, 2008 7:14 AM by Alex

The class does not work for properties and for subclasses:

public class Test

{

 private SerializableDictionary<string, string> _sd = new SerializableDictionary<string, string>();

 public Test() { _sd.Add("name", "value"); }

 public SerializableDictionary<string, string> SD { get { return _sd; } }

}

Test t = new Test();

XmlSerializer serializer = new XmlSerializer(t.GetType());

StreamWriter writer = new StreamWriter("c:\\t.xml");

serializer.Serialize(writer, t);

writer.Close();

Outputs only

 <?xml version="1.0" encoding="utf-8" ?>

- <Test xmlns:xsi="www.w3.org/.../XMLSchema-instance" xmlns:xsd="www.w3.org/.../XMLSchema">

 </Test>

# re: XML Serializable Generic Dictionary

Friday, January 18, 2008 11:52 AM by Alex

Important: readonly public variables and get-only properties cannot be serialized to XML!

# re: XML Serializable Generic Dictionary

Wednesday, January 30, 2008 7:19 PM by Gleb

You are a genious!

Thanks a lot.

# re: XML Serializable Generic Dictionary

Friday, February 01, 2008 3:48 PM by Sowokie

Im having a problem using this with TryGetValue (trying to ensure thread safety).

I have a SiteMapDataSource that i get a System.NullReferenceException on (CurrentNode==null).

       protected void menu_MenuItemDataBound(Object sender, MenuEventArgs e)

       {

          if (e.Item.Text == SiteMapDataSource 1.Provider.CurrentNode.Title)

           {

               e.Item.Text = "<strong>" + e.Item.Text + "</strong>";

           }

       }

But if i just skip the first if() everything works like a charm.

string tempCountryCode="uk";

bool contains;

           if (page.CountryCodeDictionary.TryGetValue(tempCountryCode.ToLower(), out contains))

           {

               if (SupportRoles.isAdmin(HttpContext.Current.Request.LogonUserIdentity) || page.CountryCodeDictionary[tempCountryCode])

               {

                   return true;

               }

               else

               {

                   return false;

               }

           }

           else

           {

               return false;

           }

Would be great if somebody knew what was causing this.

# re: XML Serializable Generic Dictionary

Wednesday, February 13, 2008 4:19 PM by Jason

This works great.  However, I am having one problem, when returning a Serializable dictionary from my web service, it is being treated as a DataSet.

Is there any way to have this serialize as a two dimensional array instead of a dataset.  This would also ensure that any type contained in the dictionary will have its type definition included in both the wsdl and the reference.cs web service proxy which is created on the client side.

# re: XML Serializable Generic Dictionary

Monday, February 18, 2008 6:53 PM by Gleb

I am having problems with this simple routine:

   <WebMethod()> Public Function GetBorough(ByVal iCompID As Integer) As SerializableDictionary(Of String, String)

       Dim oSearchResult As New SerializableDictionary(Of String, String)

       oSearchResult.Add("1", "one")

       Return oSearchResult

   End Function

----------------------------------------

Error is:

- The request failed with the error message:

--

System.InvalidOperationException: The top XML element 'dictionary' from namespac

e 'http://tempuri.org/' references distinct types SerializableDictionary`2[Syste

m.String,Photo] and SerializableDictionary`2[System.String,System.String]. Use X

ML attributes to specify another XML name or namespace for the element or types.

  at System.Xml.Serialization.XmlReflectionImporter.ReconcileAccessor(Accessor

accessor, NameTable accessors)

  at System.Xml.Serialization.XmlReflectionImporter.ImportElement(TypeModel mod

el, XmlRootAttribute root, String defaultNamespace)

  at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(Type type

, XmlRootAttribute root, String defaultNamespace)

  at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(Type type

, XmlRootAttribute root)

  at System.Web.Services.Description.MimeXmlReflector.ReflectReturn()

  at System.Web.Services.Description.HttpProtocolReflector.ReflectMimeReturn()

  at System.Web.Services.Description.HttpPostProtocolReflector.ReflectMethod()

  at System.Web.Services.Description.ProtocolReflector.ReflectBinding(Reflected

Binding reflectedBinding)

  at System.Web.Services.Description.ProtocolReflector.Reflect()

  at System.Web.Services.Description.ServiceDescriptionReflector.ReflectInterna

l(ProtocolReflector[] reflectors)

  at System.Web.Services.Description.ServiceDescriptionReflector.Reflect(Type t

ype, String url)

  at System.Web.Services.Protocols.DiscoveryServerType..ctor(Type type, String

uri)

  at System.Web.Services.Protocols.DiscoveryServerProtocol.Initialize()

  at System.Web.Services.Protocols.ServerProtocolFactory.Create(Type type, Http

Context context, HttpRequest request, HttpResponse response, Boolean& abortProce

ssing)

---------------------------

When I comment above function out, web service compiles fine. Any ideas anybody?  Also, this works fine with other dictionaries.

# SharePoint Conference 2008 - Day 4 &laquo; Steve Pietrek&#8217;s SharePoint Stuff

Pingback from  SharePoint Conference 2008 - Day 4 &laquo; Steve Pietrek&#8217;s SharePoint Stuff

# re: XML Serializable Generic Dictionary

Thursday, March 13, 2008 8:51 AM by Hoytster

It doesn't work for me. :(

I'm serializing to a file. The test output looks like:

<item><key><string>Key1</string></key><value><string>Value1</string></value></item>

<item><key><string>Key2</string></key><value><string>Value2</string></value></item>

<item><key><string>Key3</string></key><value><string>Value3</string></value></item>

except its not wrapped... it's all on one line.

Isn't there supposed to be a root element?

It fails on the first reader.ReadEndElement() in ReadXml(), with the exception:

"There are multiple root elements. Line 1, position 85."

That column (85) is where the second <item> tag starts.

I'm a noob, and there's no usage example. :(

I used:

XmlTextWriter writer = new XmlTextWriter(@"c:\tmp\test2.xml", System.Text.Encoding.Default);

and

XmlTextReader reader = new XmlTextReader(@"c:\tmp\test.xml");

Suggestions? I'm sure I'm just calling it wrong, since it's working for everyone else.

Thanks, Hoytster

# XML Serializable Generic Dictionary tipi | Turk Bili??im Teknolojileri Ve G??venlik Platformu | iyinet webmaster forumu 2008 seo yar????mas?? |

Pingback from  XML Serializable Generic Dictionary tipi | Turk Bili??im Teknolojileri Ve G??venlik Platformu | iyinet webmaster forumu 2008 seo yar????mas?? |

# re: XML Serializable Generic Dictionary

Thursday, April 03, 2008 11:29 AM by Greg Harris

Thanks so much. Works perfectly for what I need. Nice to see it still being found so easily years later!

# re: XML Serializable Generic Dictionary

Monday, May 05, 2008 4:38 PM by Darin

why is :

SerializableDictionary<string, string> _testdict;

       [System.Runtime.Serialization.DataMember]

       public SerializableDictionary<string, string> TestCollection

       {

           get { return _testdict; }

           set { _testdict = value; }

       }

resulting in:

[System.Xml.Serialization.XmlElementAttribute(Order=0)]

       public System.Data.DataSet TestCollection

       {

           get

           {

               return this.testCollectionField;

           }

           set

           {

               this.testCollectionField = value;

           }

       }

dont want a DATASET !!! what can i change !!! (other then that this is a great class)

# re: XML Serializable Generic Dictionary

Tuesday, May 06, 2008 6:22 AM by BrightSoul

It works wonders, thanks. It saved me a lot of time.

# re: XML Serializable Generic Dictionary

Tuesday, May 13, 2008 9:20 AM by jan wilmans

public ref class StringDict : public SerializableDictionary<System::String^, System::String^> {};

bool foobool = true;

StringDict ^ userData = gcnew StringDict();

userData["foo"] = foobool ? "1":"0";

userData["bar"] = "theone";

userData["fobia"] = "really";

XmlTextWriter ^ xmlWriter = gcnew XmlTextWriter("dict.xml", nullptr);

xmlWriter->Formatting = Formatting::Indented;

xmlWriter->WriteStartElement("addondata");

userData->WriteXml(xmlWriter);

xmlWriter->WriteEndElement();

xmlWriter->Close();

XmlTextReader ^ xmlReader = gcnew XmlTextReader("dict.xml");

StringDict ^ sd = gcnew StringDict();

xmlReader->ReadStartElement("addondata");

sd->ReadXml(xmlReader);

xmlReader->Close();

# re: XML Serializable Generic Dictionary

Tuesday, May 27, 2008 1:39 PM by Craig

I am having the same problem mentioned in several other posts on this article, that being, the class deserializes as a DataSet for some unknown reason.

Strangely enough I have used it with success elsewhere. Am I missing something small but fundamental?

# re: XML Serializable Generic Dictionary

Wednesday, May 28, 2008 10:55 AM by Surya

Using in code

-------------------------

string txt;

       using (StringWriter string_writer = new StringWriter())

       {

           xml_serializer.Serialize(string_writer, sd);

           txt = string_writer.ToString();

           string_writer.Close();

       }

                      SerializableDictionary<string, string> _sd = new SerializableDictionary<string, string>();

               using (StringReader string_reader = new StringReader(txt))

               {

                   _sd = (SerializableDictionary<string, string>)xml_serializer.Deserialize(string_reader);

               }

# re: XML Serializable Generic Dictionary

Friday, May 30, 2008 8:29 AM by Graham

Thanks... works a treat.

# re: XML Serializable Generic Dictionary

Wednesday, June 04, 2008 9:44 AM by Amer Gerzic

Thanks for the tip!

# re: XML Serializable Generic Dictionary

Wednesday, June 11, 2008 3:30 PM by Samuel

Really great work... thanks for sharing it!

# re: XML Serializable Generic Dictionary

Tuesday, June 17, 2008 1:17 PM by Charlie

This is not writing the dictionary, this is writing the contents of the dictionary. It should encapsulate the content in another element.

# XmlConverter - serialize/deserialize an object to Xml &laquo; GAnton&#8217;s Weblog

Pingback from  XmlConverter - serialize/deserialize an object to Xml &laquo; GAnton&#8217;s Weblog

# re: XML Serializable Generic Dictionary

Saturday, June 21, 2008 1:54 PM by Steve

You rock. Thank you, you saved me hours!

Leave a Comment

(required) 
(required) 
(optional)
(required)